二分查找的要点,区间能缩小为一个点
二分查找的要点就是让目标区间不断缩小直至为一个点。
这同样是一些分治算法的目标,比如快速排序,我们的目标是区间缩小为一个点,如果你不能理解这个问题,那么通常会在剩余最后两三个数的时候混乱。
避免死循环,mid的取整问题,向上or向下?
我们在二分查找的时候,要不断通过left right mid的更新去达到我们最终目标;
如果我们的mid计算方式为mid = left + (right - left) / 2,也就是mid向下取整。
那么为了能使目标区间最终能缩小为一个点,我们在更新left的时候,至少要让left前进一步,也就是left = mid + 1。此时mid变换区间为(]。
为什么?如果更新算法是left = mid,那么当left + 1 = right;时,会进入死循环,因为相邻两个数字相加除以2得到的mid=left,这时如果更新left=mid,就会进入死循环。
而此时right则没有这个问题,更新right时,无论是right = mid,还是right = mid - 1,区间最终都会缩小为一个点。
如何决定mid的取整方向,我们要看题目要求,如果left right在变换mid时,left需要更新且mid值不能舍弃,right可以舍弃,此时就只能left=mid,right=mid-1,则mid求取我们就需要向上取整。
我们来看几个二分查找的经典题目。
题目链接:https://leetcode.cn/problems/guess-number-higher-or-lower
int guessNumber(int n) {
int left = 1, right = n;
while (left < right) { // 循环直至区间左右端点相同
int mid = left + (right - left) / 2; // 防止计算时溢出
if (guess(mid) <= 0) {
right = mid; // 答案在区间 [left, mid] 中
} else {
left = mid + 1; // 答案在区间 [mid+1, right] 中
}
}
// 此时有 left == right,区间缩为一个点,即为答案
return left;
}
题目链接:https://leetcode.cn/problems/first-bad-version
int firstBadVersion(int n) {
int left = 1, right = n;
while (left < right) { // 循环直至区间左右端点相同
int mid = left + (right - left) / 2; // 防止计算时溢出
if (isBadVersion(mid)) {
right = mid; // 答案在区间 [left, mid] 中
} else {
left = mid + 1; // 答案在区间 [mid+1, right] 中
}
}
// 此时有 left == right,区间缩为一个点,即为答案
return left;
}
再来看下这个问题,这个题目中mid要求必须向上取整。
我们使用二分查找缩短枚举甜蜜度的过程中,如果mid甜蜜度满足,此时可能存在更大的甜蜜度。
此时我们需要更新left,但是mid的值不能舍弃,因为这个值可能是目标结果,所以我们只能让left=mid。
如果mid值不满足,则right必定可以舍弃,则可以让right=mid-1。
此时,为了解决left+1=right时的死循环问题,我们只能在求取mid时向上取整,公式为 (a + b - 1) / b 。
题目链接:2517. 礼盒的最大甜蜜度
bool check(vector<int>& price, int tastiness, int k) {
int cnt = 1, pre = price[0];
for (int i = 1; i < price.size(); ++i) {
if (price[i] - pre >= tastiness) {
pre = price[i];
if (++cnt == k) {
return true;
}
}
}
return false;
}
int maximumTastiness(vector<int>& price, int k) {
sort(price.begin(), price.end());
int left = 0, right = price.back() - price.front();
while (left < right) {
int mid = (left + right + 1) / 2;
if (check(price, mid, k)) {
left = mid;
} else {
right = mid - 1;
}
}
return left;
}
本文来自博客园,作者:linukey,转载请注明原文链接:https://www.cnblogs.com/linukey/p/17417205.html

浙公网安备 33010602011771号