0%

算法系列-二分查找

二分模板:

模板1

当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1,计算mid时不需要加11,即mid = (l + r)/2。

1
2
3
4
5
6
7
8
9
10
11
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = (l + r)/2;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}

模板2

当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid,此时为了防止死循环,计算mid时需要加1,即mid = ( l + r + 1 ) /2。

1
2
3
4
5
6
7
8
9
10
11
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = ( l + r + 1 ) /2;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}

为什么两个二分模板mid取值不同?

在查找的过程中,如果r更新到r = l + 1时,模板一中mid = l + l + 1 / 2 = l则l = mid = l,r还是l + 1,mid还是等于l不变,陷入死循环。所以模板二中多加1就是mid向上取整。但模板一中l = mid + 1,就不会有这种困扰

什么时候用模板1?什么时候用模板2?

主要是每次更新左边界时,是希望代码更新成l = mid,就mid要向上取整,如果l = mid + 1,就不用向上取整。

  • 二分查找时,首先要确定我们要查找的边界值,保证每次二分缩小区间时,边界值始终包含在内。然后再选择使用什么代码来写

剑指 Offer 53 - I. 在排序数组中查找数字 I

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
int search(vector<int>& nums, int target) {
if(nums.empty()) return 0;
// 先找坐标最小的target位置
int l = 0, r = nums.size() - 1;
while(l < r) {
int mid = (l + r ) / 2;//如果这里是l+r+1,则如果最后让r = l + 1且nums[r] = target,则陷入死循环
if(nums[mid] >= target) { //因为这里想找target对应位置比较小的坐标,所以就算=target,也要往左缩小区间,改变r
r = mid;
}
else l = mid + 1;
}
if(nums[r] != target) return 0;// 如果没有target,直接返回0
int left = r;
l = 0, r = nums.size() - 1;
while(l < r) {
int mid = (l + r + 1) / 2;//在改变l时,如果取下界,r=l+1,则mid = l,mid又赋给l,死循环
if(nums[mid] <= target) {//因为这里想找target对应位置比较大的坐标,所以就算mid=target,也要往右缩小区间,改变l
l = mid;
}
else r = mid - 1;
}
int right = r;
return right - left + 1;
}
};