白银-二分查找经典问题

1.二分查找的应用

1.1.在排序数组中查找元素的第一个和最后一个位置

思路:

  1. 两次二分查找,一次寻找左边界,一次寻找右边界。
  2. 技巧:寻找左边界用high,寻找右边界用low
  3. 注意点:最后得到的左右边界与实际边界差一
  4. 有三种情况:情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
    情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
    情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int leftBorder = getLeftBorder(nums,target);
        int rightBorder = getRightBorder(nums,target);
        if(leftBorder == -2 || rightBorder == -2) return new int[]{-1,-1};
        if(rightBorder - leftBorder > 1) return new int[]{leftBorder+1,rightBorder-1};
        else return new int[]{-1,-1};
    }
    public int getRightBorder(int[] nums,int target){
        int low = 0;
        int high = nums.length - 1;
        // target不在数组中时的默认值
        int rightBorder  = -2;
        while(low <= high){
            int mid = low + ((high - low) >> 1);
            if(nums[mid] > target) high = mid - 1;
            else {
                low = mid + 1;
                rightBorder  = low;
            }
        }

        return rightBorder;
    }
    public int getLeftBorder(int[] nums,int target){
        int low = 0;
        int high = nums.length - 1;
        // target不在数组中时的默认值
        int leftBorder  = -2;
        while(low <= high){
            int mid = low + ((high - low) >> 1);
            if(nums[mid] >= target){
                high = mid - 1;
                leftBorder  = high;
            }
            else {
                low = mid + 1;
            }
        }

        return leftBorder;
    }
}

2.基于二分查找的拓展问题

2.1.山脉数组的峰顶索引

image-20230826181840376

二分查找的变形,峰顶前升序,峰顶后降序。

  1. 在上升阶段,low = mid + 1;
  2. 在下降阶段,high= mid - 1;
  3. 在峰顶,return;
class Solution {
    public int peakIndexInMountainArray(int[] arr) {
        if(arr.length == 3){
            return 1;
        }
        int low = 1;
        int high = arr.length - 2; 
        while(low <= high){
            int mid = low + ((high - low) >> 1);
            // 在上升阶段
            if(arr[mid] > arr[mid-1] && arr[mid] < arr[mid+1]) low = mid + 1;
            // 在峰顶
            else if(arr[mid] > arr[mid-1] && arr[mid] > arr[mid+1]) return mid;
            // 在下降阶段
            else high = mid - 1;
        }
        return low;
    }
}
2.2.旋转数字的最小数组

image-20230826204540874

思路:

数组经过旋转后会分成两段递增的数组,如下图:

image-20230826211124858

用nums[high]来作比较

如果 中间值小于nums[high],最小值在左侧,舍弃右侧

如果中间值大于nums[high],最小值在右侧,舍弃左侧

跟普通二分相比,只是将中间值和目标值比较换成了中间值和high处的元素比较.

class Solution {
    public int findMin(int[] nums) {
        int low = 0;
        int high = nums.length - 1;
        while(low <= high){
            int mid = low + ((high - low) >> 1);
            // 中间值小于nums[high],说明mid在上升阶段,最小值在左侧,舍弃右侧
            if(nums[mid] < nums[high]) high = mid;
            else{
                low = mid + 1;
            }
        }
        // 因为循环条件为<=,所以在找到中间值后会再执行一次mid+1
        return nums[low - 1];
    }
}
2.3.0-n-1缺失的数字

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IE81mn5v-1693230285841)(C:\Users\李建伟llx\AppData\Roaming\Typora\typora-user-images\image-20230826213000301.png)]

思路:

  1. 有序数组加查找,首先想到二分查找;
  2. 规律:如果没有缺失数字,那么在这个递增数字中有下标等于对应的元素(nums[i] == i);
  3. 如果有缺失数字,那么下标不等于对应的元素(nums[i] != i
class Solution {
    public int missingNumber(int[] nums) {
        int low = 0;
        int high = nums.length - 1;
        while(low <= high){
            int mid = low + ((high - low) >> 1);
            // mid之前连续
            if(nums[mid] == mid){
                low = mid + 1;
            }
            else {
                high = mid - 1;
            }
        }
        return low;
    }
}
2.4.二分法求平方根

image-20230826221743991

class Solution {
    public int mySqrt(int x) {
        int low = 0;
        int high = x;
        int res = 0;
        while(low <= high){
            int mid = low + ((high - low) >> 1);
            if((long) mid * mid <= x){
                res = mid;
                low = mid + 1;
            }
            else {
                high = mid - 1;
            }
        }
        return res;
    }
}

2.中序与搜索树原理

二叉搜索树:左子树的值均小于根节点的值;右子树的值都大于根节点的值。

二叉搜索树的中序序列是一个递增序列。

2.1.二叉搜索树中的搜索

image-20230827132302793

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root == null || root.val == val) return root;
        return val < root.val ? searchBST(root.left,val) : searchBST(root.right,val);
    }
}
2.2.验证二叉搜索树

类似中序遍历

二叉搜索树的中序序列是一个递增序列。所以只需要判断当前节点的值是否大于前一个遍历到的节点即可

class Solution {
    // 中序遍历,二叉搜索树为升序
    long pre = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        if(root == null) return true;
        boolean left = isValidBST(root.left);
        if(!left) return false;
        if(pre >= root.val) return false;
        pre = root.val;
        boolean right = isValidBST(root.right);
        return right;
    }
}
2.3.二叉搜索树的最小绝对差

image-20230827135338235

用一个pre保存中序遍历的前一个结果,

class Solution {
    int pre;
    int ans;
    public int getMinimumDifference(TreeNode root) {
        ans = Integer.MAX_VALUE;
        pre = -1;
        dfs(root);
        return ans;
    }
    public void dfs(TreeNode root){
        if(root == null) return;
        dfs(root.left);
        // 赋初值
        if(pre == -1){
            pre = root.val;
        }else{
            ans = Math.min(ans,root.val - pre);
            // 更新前驱节点的值
            pre = root.val;
        }
        dfs(root.right);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值