二分法模板——y总
参考链接:二分查找算法模板 - AcWing
二分法:是一种在有序数组中查找给定关系元素的算法。
算法思路:假设目标值在闭区间[left, right]中,每次将区间折半,当lleft==right时,就找到了目标值。
二分法的边界条件令人头痛,
while(left<=right) 还是 while(left<right)
left=mid 还是left=mid+1
…
y总给出了两个版本的二分法查找模板,用于不同情况,退出条件均为 while(left<right)
版本一
**适用于:**查找有序数组中的左边界。列如:在升序数组中找到第一个不小于target的索引。
nums = [5,7,7,8,8,10], target = 8;
ans = 3;
int bsearch_left(vector<int> &nums, int target)
{
if (nums.empty()) return -1;
int left = 0, right = nums.size() - 1; //向下取整
while (left < right)
{
int mid = left + (right - left) / 2;
if (nums[mid] >= target) right = mid;
else left = mid + 1;
}
return left;
}
上示代码,只有 nums[i] < target 时才会更新 left = mid + 1,那么只会有下图示的两种情况:
- 若某次 mid 取到了 ① 位置 ,则 left 先于right到达目标位置②,后续只会更新 right,直至 right 指向位置③,在下一次循环中,由于int mid = left + (right - left) / 2; 是向下取整,mid会落在left,更新 right = mid 后 left 和 right 最后都会指向位置②;
- 另一种情况就是 mid 未取到了 ① 位置,right 先于 left 到达位置②,后续只会更新 left,直至 left指向位置 ①,在下一次循环中,由于int mid = left + (right - left) / 2; 是向下取整,mid会落在left,更新 left = mid + 1,使 left 也会指向位置②。
版本二
**适用于:**查找有序数组中的右边界。列如:在升序数组中找到最后一个不大于target的索引。
nums = [5,7,7,8,8,10], target = 8;
ans = 4;
int bsearch_right(vector<int> &nums, int target)
{
if (nums.empty()) return -1;
int left = 0, right = nums.size() - 1;
while (left < right)
{
int mid = left + (1 + right - left) / 2; //向上取整
if (nums[mid] <= target) left = mid;
else right = mid - 1;
}
return left;
}
上示代码,只有 nums[i] > target 时才会更新 right= mid - 1,那么只会有下图示的两种情况:
-
若某次 mid 取到了 ③ 位置 ,则 right先于left到达目标位置②,后续只会更新 left,直至 left指向位置①,在下一次循环中,由于int mid = left + (1 + right - left) / 2; 是向上取整,mid会落在 right ,更新 left = mid 后 left 和 right 最后都会指向位置②;
-
另一种情况就是 mid 未取到 ③ 位置,left先于 right到达位置②,后续只会更新 right,直至 right指向位置 ③,在下一次循环中,由于int mid = left + (right - left) / 2; 是向上取整,mid会落在right,更新 right = mid - 1, 使 right 也会指向位置②。
案例
34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode) (leetcode-cn.com)
题中,给定一个升序数组,找出目标值在数组中的开始位置和结束位置,实质就是找到升序数组中第一个不小于(即大于等于)目标值的索引,和最后一个不大于(即小于等于)目标值的索引。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.empty()) return { -1,-1 };
vector<int> ans(2, -1);
int left = 0, right = nums.size() - 1;
while (left < right)
{
int mid = left + (right - left) / 2;
if (nums[mid] >= target) right = mid;
else left = mid + 1;
}
if (nums[left] != target) return ans; //未找到目标值
ans[0] = left;
left = 0;
right = nums.size() - 1;
while (left < right)
{
int mid = left + (1 + right - left) / 2;
if (nums[mid] <= target) left = mid;
else right = mid - 1;
}
ans[1] = left;
return ans;
}
};```