二分查找框架

146 阅读2分钟

概述

  • 二分查找分三种
    • 查找某个确定的值
    • 查找左边界
    • 查找右边界
  • 本质
    • 二分的本质是缩小区间
  • 难点:
    • 最难的点在于大于小于符号的界定,有多种写法,单纯记很难,这里做一次总结

规定

  • 这里限定几个规定,按照这种套路写
// 左右都是闭区间
left = 0, right = nums.length-1;
// while(left <= right) 后面条件都是
while(left <= right)

查找确定值

  • 退出条件是 right 在 left 的左边, 这时候就是不存在这个值
int binary_search(int[] nums, int target) {
    int left = 0, right = nums.length - 1; 
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1; 
        } else if(nums[mid] == target) {
            // 直接返回
            return mid;
        }
    }
    // 直接返回
    return -1;
}

查找左边界

  • 同样基本上和查找某个值框架相同
  • 两个不同点
    • 等于条件时候, right = mid -1,这个直接画个图就直到呢

    • 记住一点 二分的本质是缩小区间

    • 需要判断越界情况

  • 为什么返回的是 left 而不是 right ,看图,退出循环条件是 left> right image.png
  • 为什么 相等条件的时候是 right = mid - 1, 如图所示
    • 由于我们确定的都是闭区间 [0,nums.length-1] ,所以必然是 left = mid+1 或者right= mid-1,
    • 如果选定的是闭区间, 在相等情况下 left = mid 或者是 right = mid ,会导致不能退出循环
    • 所以在相等情况下,肯定是 left = mid +1 或者是 right = mid -1
    • 我们知道,二分查找的本质是排除掉不需要的区间
    • 如果 选定的是 left = mid + 1,那么 left 将指向 第二个 2 的位置,下一次循环,left 将指向 3 所在的位置,而我们需要排除的就是右侧的区间,所以用脚丫子想都知道是错的(这是排除左侧的区间,找的是右侧的边界)
    • 所以 找左侧边界,nums[mid] = target 相等时候 是 right = mid -1 ,排除右侧无用区间

image.png

int left_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 别返回,锁定左侧边界
            right = mid - 1;
        }
    }
    // 最后要检查 left 越界的情况
    if (left >= nums.length || nums[left] != target)
        return -1;
    return left;
}

右边界

  • 和左边界基本上完全相同的代码,就是相等排除的是左侧区间
  • 记住需要这边需要返回的是right

image.png


int right_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 别返回,锁定右侧边界
            left = mid + 1;
        }
    }
    // 最后要检查 right 越界的情况
    if (right < 0 || nums[right] != target)
        return -1;
    return right;
}