二分查找与搜索树的高频问题

21 阅读6分钟

基于二分查找的扩展问题

山脉数组的峰顶索引

山脉数组的峰顶索引 符合下列属性的数组 arr 称为 山脉数组

  • arr.length >= 3

  • 存在 i(0 < i < arr.length - 1)使得:

    • arr[0] < arr[1] < ... arr[i-1] < arr[i]
    • arr[i] > arr[i+1] > ... > arr[arr.length - 1]

给你由整数组成的山脉数组 arr ,返回满足 arr[0] < arr[1] < ... arr[i - 1] < arr[i] > arr[i + 1] > ... > arr[arr.length - 1] 的下标 i 。 你必须设计并实现时间复杂度为 O(log(n)) 的解决方案。

示例 1: 输入:arr = [0,1,0] 输出:1

示例 2: 输入:arr = [0,2,1,0] 输出:1

示例 3: 输入:arr = [0,10,5,2] 输出:1

class Solution {
    public int peakIndexInMountainArray(int[] arr) {
        
        int l=0;
        int r=arr.length-1;
        while (l<=r){
            int m=l+((r-l)>>1);
            if(arr[m]>arr[m+1]&&arr[m]>arr[m-1]){
                return m;
            }else if(arr[m]>arr[m+1]&&arr[m-1]>arr[m]){
                r=m-1;
            }else {
                l=m+1;
            }
        }
        return -1;

    }
}

旋转数字的最小数字

旋转数字的最小数字 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]

  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]]旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。 给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。 你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1: 输入:nums = [3,4,5,1,2] 输出:1 解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2: 输入:nums = [4,5,6,7,0,1,2] 输出:0 解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。

示例 3: 输入:nums = [11,13,15,17] 输出:11 解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。

image.png

class Solution {
    public int findMin(int[] nums) {
        int l=0;
        int r=nums.length-1;
        while (l<r){
            int m=l+((r-l)>>1);
            if(nums[m]<nums[r]){
                r=m;
            }else {
                l=m+1;
            }
        }
        return nums[l];
    }
}

找缺失的数据

:::info 剑指offr题目:一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0n-1 之内。在范围0n-1内的个数字中有且只有一个数字不在该数组中,请找出这个数字。 这个题很简单是不?从头到尾遍历一遍即可确定,但是这么简单肯定不是面试需要的。那这个题要考什么 呢?就是二分查找。 对于有序的也可以用二分查找,这里的关键点是在缺失的数字之前,必然有ums[i]==i,在缺失的数字之 后,必然有nums!=i。 因此,只需要二分找出第一个ums[i]!=i,此时下标就是答案。若数组元素中没有找到此下标,那么缺失 的就是n。代码如下: :::

class Solution {
    public static void main(String[] args) {
        int[] nums=new int[]{0,1,2,3,5,6};
        System.out.println(missingNumber(nums));
    }
    public static int missingNumber(int[] nums) {
        int l=0;
        int r=nums.length-1;
        while (l<r){
            int m=l+((r-l)>>1);
            if(nums[m]==m){
                l=m+1;
            }else {
                r=m;
            }
        }
        return l;
    }
}

优化平方根

x的平方根 给你一个非负整数 x ,计算并返回 x 的 算术平方根 。 由于返回类型是整数,结果只保留 **整数部分 **,小数部分将被 舍去 。 **注意:**不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

示例 1: 输入:x = 4 输出:2

示例 2: 输入:x = 8 输出:2 解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去

class Solution {
    public static void main(String[] args) {
        mySqrt(8);
    }
    public static int mySqrt(int x) {
        if(x==0) return 0;

        int l=1;
        int r=x;
        while (l<=r){
            int m=l+((r-l)>>1);
            if(x/m>m){
                l=m+1;
            }else if(x/m<m&&x/(m-1)>m-1){
                return m-1;
            }else if(x/m<m){
                r=m-1;
            }else if(x/m==m) {
                return m;
            }
        }
        return l;

    }
}

中序与搜索树原理

:::info 在前面我们发现很多题使用前序、后序或者层次遍历都可以解决,但几乎没有中序遍历的。这是因为中序 与前后序相比有不一样的特征,例如中序可以和搜索树结合在一起,但是前后序则不行。 二叉搜索树是一个很简单的概念,但是想说清楚却不太容易。简单来说就是如果一棵二叉树是搜索树,则 按照中序遍历其序列正好是一个递增序列。比较规范的定义是: ·若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值, ·若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值, ·它的左、右子树也分别为二叉排序树。下面这两棵树一个中序序列是{3,6,9,10,14,16,19},一个是 {3,6,9,10},因比都是搜索树: ::: image.png

二叉搜索树中搜索特定值

二叉搜索树中搜索特定值 给定二叉搜索树(BST)的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。

示例 1: 输入:root = [4,2,7,1,3], val = 2 输出:[2,1,3]

示例 2: 输入:root = [4,2,7,1,3], val = 5 输出:[]

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root.val==val) {
            return root;
        }else if(root.val>val){
            if(root.left==null) return null;
            return searchBST(root.left,val);
        }else {
            if(root.right==null) return null;
            return searchBST(root.right,val);
        }

    }
}

验证二叉搜索树

验证二叉搜索树 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下:

  • 节点的左子树只包含** 小于 **当前节点的数。

  • 节点的右子树只包含 大于 当前节点的数。

  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1: 输入:root = [2,1,3] 输出:true

示例 2: 输入:root = [5,1,4,null,null,3,6] 输出:false 解释:根节点的值是 5 ,但是右子节点的值是 4 。

class Solution {

     long pre=Long.MIN_VALUE;
    public  boolean isValidBST(TreeNode root) {
        if(root==null) return true;
        if(!isValidBST(root.left)){
            return false;
        }
        if(root.val<=pre){
            return false;
        }
        pre=root.val;
        return isValidBST(root.right);

    }



}