LeetCode刷题笔记——二分查找

LeetCode刷题笔记——二分查找

细节问题

  • 不等号是否应该取等于
  • mid是否应该加一

二分查找框架

int binarySearch(int[] nums, int target) {
    int left = 0, right = ...;

    while(...) {
        int mid = (right + left) / 2;
        if (nums[mid] == target) {
            ...
        } else if (nums[mid] < target) {
            left = ...
        } else if (nums[mid] > target) {
            right = ...
        }
    }
    return ...;
}

寻找一个数

int binarySearch(int[] nums, int target) {
    int left = 0; 
    int right = nums.length - 1; // 注意

    while(left <= right) { // 注意
        int mid = (right + left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; // 注意
        else if (nums[mid] > target)
            right = mid - 1; // 注意
        }
    return -1;
}
  1. 为什么 while 循环的条件中是 <=,而不是 < ?
    答:因为初始化 right 的赋值是 nums.length - 1,即最后一个元素的索引,而不是 nums.length
    这二者可能出现在不同功能的二分查找中,区别是:前者相当于两端都闭区间 [left, right],后者相当于左闭右开区间 [left, right),因为索引大小为 nums.length 是越界的。
  2. 什么时候停止搜索?
    • 找到了 if(nums[mid]==target)

    • 循环终止,返回-1.

      • 那么什么时候循环终止?答:搜索区间为空
    • 什么时候搜索区间为空(初始化差异,分为两种情况)
      主要在于计算mid的时候会不会越界。我们知道,mid的计算公式如下:

      int mid=left+(right-left)/2;

      如果我们初始化的right是不能取到的,然后循环的结束条件设成left<=right,当最后一个循环时,有leftright,那么,计算得到的midleft==right,就是一个越界的值,会导致计算错误。所以这种情况下,我们应该将right初始化成能取到的最后一个数。

      如果我们初始化的right是能取到的,然后循环的结束条件设成left,那么最后一个循环哟,left+1=right,那么计算得到mid=left+(right-left)/2=left=right-1;很明显,我们的搜索区间会漏掉最后一个数。

因此,我们总结出以下搭配:
- while(left <= right) 闭区间
终止条件是 left == right + 1,写成区间的形式就是[right + 1, right],或者带个具体的数字进去[3, 2],可见这时候搜索区间为空,因为没有数字既大于等于 3 又小于等于 2 的吧。所以这时候 while 循环终止是正确的,直接返回 -1 即可。
- while(left < right) 左闭右开区间
终止条件是left == right,写成区间的形式就是[right, right],或者带个具体的数字进去 [2, 2],这时候搜索区间非空,还有一个数 2,但此时 while 循环终止了。也就是说这区间 [2, 2]被漏掉了,索引 2 没有被搜索,如果这时候直接返回 -1 就可能出现错误。
**总结:**寻找一个数的时候,写成while(left <= right)

寻找左侧边界

int left_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length; // 注意

    while (left < right) { // 注意
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid; // 注意
        }
    }
    return left;
}

问题:

  1. 收缩右侧为什么是right=mid而不是right=mid-1;因为搜索区间是左闭右开的。每次搜索我们都将mid从搜索区间中排除。对于left,mid能取到,所以left=mid+1,对于right,mid取不到,所以right=mid
  2. 返回left和返回right?
    结果一样。因为循环结束时left==right
  3. 不存在时如何处理?
    循环结束,我们判断nums[left]是否等于target

寻找右侧边界

你可能感兴趣的:(leetcode)