二分查找的模板与边界问题

二分查找的模板与边界问题

二分查找有两套模板,模板A代码如下:

1
2
3
4
5
6
7
8
9
10
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;
}

模板B代码如下:

1
2
3
4
5
6
7
8
9
10
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;
}

两套代码有什么区别

  • 模板A区间[l,r]划分成[l,mid]和[mid+1,r],更新操作是r=mid或者l=mid+1,计算mid时不需要加1,即mid=(l+r)/2
  • 模板B区间[l,r]划分成[l,mid-1]和[mid,r],更新操作是r=mid-1或者l=mid,计算mid时不需要加1,即mid=(l+r+1)/2

为什么使用两套模板的mid计算方法不同?

当使用模板B时,会有以下这种特殊情况:r=l+1。此时,如果使用mid=(l+r)/2,向下取整,使得mid=l,从而形成无限循环。

什么时候用模板A?什么时候用模板B?

左边界要更新为l=mid+1,使用模板A。 左边界要更新为l=mid,则使用模板B。

数据溢出怎么办?

把mid的计算方式更改为mid=l+((r-l)/2)

Example

你现在已经完全掌握二分查找了,快来试试看吧:leetcode-33. 搜索旋转排序数组 示例代码:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
int search(vector<int>& nums, int target) {
//使用模板A,找到旋转点,用p储存之
int l = 0, r = nums.size() - 1;
while (l < r)
{
int mid = (l + r)/2;
if (nums[mid] < nums[r])
r = mid;
else l = mid + 1;
}
int p = l;

//[0,p]为有序数组,使用模板A二分查找
r = p - 1, l = 0;
while (l < r)
{
int mid = (l + r)/2;
if (nums[mid] >= target)
r = mid;
else l = mid + 1;
}
if(nums[l] == target)
return l;

//p之后为有序数组,使用模板A二分查找
l = p, r = nums.size() - 1;
while (l < r)
{
int mid = (l + r)/2;
if (nums[mid] >= target)
r = mid;
else l = mid + 1;
}
if(nums[l] == target)
return l;
else
return -1;
}
};