二分查找
三种区间:
-
闭区间 [ ]
-
开区间 ( )
-
半闭半开区间 [ ),( ]
四种比较:
- 大于等于 ≥
- 大于 >
- 小于 <
- 小于等于 ≤
例题:返回有序数组中第一个 ≥ 8的数的位置。如果所有数都 <8,返回数组长度。
闭区间 [ ]做法
L = 0
M = (L + R) / 2
R = n - 1
关键:循环不变量
三种区间的代码
// 闭区间写法
int lower_bound(vector<int> &nums, int target) {
int left = 0, right = (int)nums.size() - 1;// 闭区间 [left, right]
while(left <= right) { // 区间不为空
// 循环不变量:
// nums[left-1] < target
// nums[right+1] >= target
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid + 1; // 范围缩小到 [mid+1, right]
else
right = mid - 1; // 范围缩小到 [left, mid-1]
}
return left; // 或者 right + 1
}
// 左闭右开区间写法
int lower_bound2(vector<int> &nums, int target) {
int left = 0, right = nums.size(); // 左闭右开区间 [left, right)
while (left < right) { // 区间不为空
// 循环不变量:
// nums[left-1] < target
// nums[right] >= target
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid + 1; // 范围缩小到 [mid+1, right)
else
right = mid; // 范围缩小到 [left, mid)
}
return left; // 或者 right
}
// 开区间写法
int lower_bound3(vector<int> &nums, int target) {
int left = -1, right = nums.size(); // 开区间 (left, right)
while (left + 1 < right) { // 区间不为空
// 循环不变量:
// nums[left] < target
// nums[right] >= target
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid; // 范围缩小到 (mid, right)
else
right = mid; // 范围缩小到 (left, mid)
}
return right; // 或者 left+1
}
四种比较的替换
如果数组内元素都是整数,则可以做如下替换
-
大于等于 ≥ x
-
大于 > 替换为: (≥ x) + 1
-
小于 < 替换为: (≥ x) - 1
-
小于等于 ≤ 替换为: (> x) - 1 再替换为:(≥ x+1)- 1
例题:
34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(Leetcode)
找给定目标值在有序数组中的开始位置和结束位置,即找“大于等于”和“小于等于”
public:
// 下面lower_bound是自定义的
vector<int> searchRange(vector<int> &nums, int target) {
// 使用其中一种写法即可
int start = lower_bound(nums, target); // 大于等于
if (start == nums.size() || nums[start] != target)
return {-1, -1};
// 如果 start 存在,那么 end 必定存在
int end = lower_bound(nums, target + 1) - 1; // 小于等于的转换
return {start, end};
}
};