这是我参与「第三届青训营 -后端场」笔记创作活动的第4篇笔记
理解下面这4种情况的二分查找,那么你就再也不会在二分查找这里出错了!
二分的核心就是四种情况
其中选择(l + r)还是(l+r+1)取决于你想逼近左边还是逼近右边 mid = (l + r) / 2 还是 mid = (l + r + 1) / 2 取决于你想逼近左边还是右边,这两类是不同的二分!
二分模板-来自于yxc
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
实战的四种情况(以vector举例)
vector<int> a = {1, 2, 2, 3, 3, 3, 4, 4, 5, 6};
// 4类二分!万物均源自于该4类,其中1和3算同类,2和4算同类
// mid = (l + r) / 2 还是 mid = (l + r + 1) / 2 取决于你想逼近左边还是右边,这两类是不同的二分
// 1.找到第一个3的前一个位置
int l = 0, r = a.size() - 1;
while(l < r){
int mid = (l + r + 1) / 2;
if(a[mid] < 3){
l = mid;
}else{
r = mid - 1;
}
}
cout<<l<<endl;
// 2.找到第一个3的位置
int l = 0, r = a.size() - 1;
while(l < r){
int mid = (l + r) / 2;
if(a[mid] >= 3){
r = mid;
}else{
l = mid + 1;
}
}
cout<<l<<endl;
// 3.找到最后一个3的位置
int l = 0, r = a.size() - 1;
while(l < r){
int mid = (l + r + 1) / 2;
if(a[mid] <= 3){
l = mid;
}else{
r = mid - 1;
}
}
cout<<l<<endl;
// 4.找到最后一个3的后一个位置
int l = 0, r = a.size() - 1;
while(l < r){
int mid = (l + r) / 2;
if(a[mid] > 3){
r = mid;
}else{
l = mid + 1;
}
}
cout<<l<<endl;