【Java】二分法查找数字/查找左右边界代码分析

二分法lo、hi、mid变来变去很烦,容易出错,所以记下

1.升序查找某个数字

num为一个升序数组,target为要查找的数字,找到了则返回索引下标,否则返回-1

public int search(int[] num, int target) {
    
    
        int lo = 0, hi = num.length;
        while (lo < hi) {
    
    
            int mid = lo + (hi - lo)/2;
            if (num[mid] == target)
                return mid;
            else if (num[mid] > target)
                hi = mid;
            else if (num[mid] < target)
                lo = mid + 1;
        }
        return -1;
    }

细节

lo < hi:采取左闭右开进行搜索[lo, hi)内是否有target

while(lo < hi)

  • lo = hi - 1时,此时搜索区间为[lo, lo + 1),区间内只有一个数,且hi - lo = 1,则mid = lo + 1/2 = lo。这个数同时被lomid指向,随后判断 if (num[mid] == target),满足则找到,不满足下一轮则进入lo == hi

  • lo == hi时会退出循环,此时搜索区间为[lo,lo),区间不存在,所以返回-1

2.寻找左侧闭边界

arr为一个升序数组,target为要查找的数字,求arr>=target的第一个数的下标

private int leftBound(int lo, int hi, int[] arr, int target) {
    
    
        while (lo < hi) {
    
    
            int mid = lo + (hi - lo)/2;
            if (arr[mid] == target) {
    
    
                hi = mid;
            } else if (arr[mid] > target) {
    
    
                hi = mid;
            } else if (arr[mid] < target) {
    
    
                lo = mid + 1;
            }
        }
        return lo;
    }

细节

lo < hi:采取左闭右开进行搜索[lo, hi)内区间

arr[mid] == target

  • 找到target,没有直接返回下标,因为可能出现重复,如下图,target=1在这里插入图片描述
    此时应该将hi收缩,让搜索区域靠近图中数字1出现的第一个位置,即hi = mid粉红色区域代表搜索区间
    在这里插入图片描述
    return lo:当lo == hi时搜索区间为循环退出
    • 第一种情况:所有判断都执行过,hi是由 hi = mid而来,且满足arr[mid] >= target,可得arr[hi]>=target,那么lo=hi搜索区间为空了,得到的一定是右边界
    • 第二种情况:只执行过第三个else if,说明target大于数组中所有值,所以返回lo

3.求右侧闭边界

arr为一个升序数组,target为要查找的数字,求arr<=target的开始位置下标

private int leftBound(int lo, int hi, int[] arr, int target) {
    
    
        while (lo < hi) {
    
    
            int mid = lo + (hi - lo)/2;
            if (arr[mid] == target) {
    
    
                lo = mid + 1;
            } else if (arr[mid] < target) {
    
    
            	lo = mid + 1;
            } else if (arr[mid] > target) {
    
    
                hi = mid;
            }
        }
        return lo - 1;
    }

细节

lo < hi:采取左闭右开进行搜索[lo, hi)区间

arr[mid] == target

  • 找到target,没有直接返回下标,因为可能出现重复,如下图,target=1
    在这里插入图片描述
    此时应该将lo收缩,让搜索区域靠近图中数字1出现的最后一个位置,即lo = mid + 1
    在这里插入图片描述
    return lo - 1:当lo == hi时搜索区间为循环退出
    • 第一种情况:所有判断执行过,lo是由 lo = mid + 1而来,且满足arr[mid] <= target,那么当lo减去1,则满足条件,arr[lo - 1] <= target,所以返回lo - 1
    • 第二种情况:只执行过第三个else if,说明target小于数组中所有值不包括arr[lo],所以返回lo - 1

总结

  • 首先确定边界
  • 返回值分析,比如查找左边界(arr中>=target的第一个数)无非就三种情况(左闭右开):
  1. 所有的值都小于targethilo指向最后一个数字(处于开区间上的那个数字,这个数字并不在我们的搜索区间内)
  2. 所有的值都大于targethilo指向第一个数字
  3. 除了上面两种,hilo指向的是>=target第一个数字
  • 具体情况要具体处理

猜你喜欢

转载自blog.csdn.net/qq_43709922/article/details/112146401