lowerBound
方法
lowerBound
方法在数组arr
中寻找第一个大于等于(不小于)target
的元素的位置。
如果数组中存在target
,它会返回第一个target
出现的位置;
如果不存在,它会返回第一个大于target
的元素的位置。
如果数组中的所有元素都小于target
,那么它将返回数组的长度,表示target
应该被插入到数组的末尾。
初始化
设置两个指针l
和r
,分别指向数组的起始位置和结束位置。即l = 0
,r = arr.length - 1
。
循环不变量
循环不变量指在循环的每次迭代之前、之后以及循环结束时都保持为真的性质或条件。
始终保持以下循环不变量:
- 如果目标位置存在于数组
arr
中,那么它一定在当前查找区间[l, r]
内。 (-∞, l-1]
一定小于target
,[r+1, +∞]
一定大于等于target
。
维护
在循环的每一步中,计算中间位置mid = l + (r - l) / 2
。
比较arr[mid]
与target
的值:
- 如果
arr[mid] == target
,则更新r
指针,使其指向mid - 1
,这样我们可以在左侧继续寻找第一个target
。 - 如果
arr[mid] > target
,则更新r
指针,使其指向mid - 1
,因为target
只能在左侧区间。 - 如果
arr[mid] < target
,则更新l
指针,使其指向mid + 1
,因为target
只能在右侧区间。
终止
循环终止的条件是l
大于r
,即l - 1 = r
。此时,循环不变量帮助我们得出最终结果:
当循环结束时,如果
target
存在于数组中,那么它一定在[l, r]
区间内。但由于l
大于r
,区间为空,相当于遍历完全所有可能结果,这意味着我们找到了target
或者确定了target
应该插入的位置。根据循环不变量:
(-∞, l-1]
一定小于target
,[r+1, +∞]
一定大于等于target
。得出此时
l
不满足小于target
,r
不满足大于等于target
,r+1
为第一个大于等于target
的位置,又因为l - 1 = r
,则l
可以作为结果。
因此,当循环结束时,l
就是我们要找的位置,可以直接返回l
作为结果。
查看代码
java
public int lowerBound(int[] arr, int target) {
int l = 0, r = arr.length - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (arr[mid] > target) {
r = mid - 1;
} else if (arr[mid] < target) {
l = mid + 1;
} else {
r = mid - 1; // 当找到target时,继续在左侧寻找第一个target
}
}
return l;
}