二分法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
。这个数同时被lo
和mid
指向,随后判断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
的第一个数)无非就三种情况(左闭右开):
- 所有的值都小于
target
,hi
和lo
指向最后一个数字(处于开区间上的那个数字,这个数字并不在我们的搜索区间内) - 所有的值都大于
target
,hi
和lo
指向第一个数字 - 除了上面两种,
hi
和lo
指向的是>=target
的第一个数字
- 具体情况要具体处理