二分的时间复杂度
二分查找的平均时间复杂度和最坏时间复杂度均为o(logn)
二分的本质是什么?
二分的本质并不是单调性,二分的本质是边界。
数据结构书上面的代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int a[N];
int n;
int BinarySearch( int x, int &cnt)
{
int l = 0, r = n - 1;
while(l <= r){
cnt++;
int mid = (l + r) >> 1;
if(a[mid] > x) r = mid - 1;
else if(a[mid] < x) l = mid + 1;
else return mid;
}
return -1;
}
int main()
{
cin >> n;
for(int i=0;i<n;i++) cin >> a[i];
int x;cin >> x;
int cnt = 0;
int indix = BinarySearch(x,cnt);
cout<<indix<<'\n'<<cnt;
return 0;
}
书上代码和接下来的代码的不同
书上面的代码只是用于确切的查找某一个元素是在数组里面的下标
而接下来给出的代码模板是用于查找分界点。也就是用来找到数组中第一个等于target
的元素的位置。这个模板确保即使存在多个相同的值,也能返回最左边的那个,也就是第一个出现的位置。或者可以用来查找最后一个等于target
的元素的位置。
二个不同的模板:(根据考虑是左边界还是右边界来选择模板)
在解决这种二分查找问题时,重要的是清晰地定义我们要找的目标边界是什么,并明确何时移动左边界l
和右边界r
。
寻找左半区符合性质的右边界点(性质在左半区符合,在右半区不符合)
目标:找到左半区最右侧的符合性质的点;
- 如果
mid
点符合性质:说明mid
位于左半区,我们需要包含mid
继续向右查找,因此更新左边界l = mid
。 - 如果
mid
点不符合性质:说明mid
已经位于右半区,所以我们需要向左查找,更新右边界r = mid - 1
。(不能包含mid,mid在右半部分,肯定不符合性质,所以要从r-1开始查找边界)
寻找右半区符合性质的左边界点(性质在右半区符合,在左半区不符合)
目标:找到右半区最左侧的符合性质的点
- 如果
mid
点符合性质:说明mid
位于右半区,我们需要包含mid
继续向左查找,因此更新右边界r = mid
。 - 如果
mid
点不符合性质:说明mid
位于左半区,所以我们需要向右查找,更新左边界l = mid + 1
。
为什么对于第一个模板需要(l+r+1)/2呢?
当l=r - 1的时候,如果mid =(l+r)/2,此时mid = l,如果check是true,那就将l = mid = l ,那么就会进入死循环(l < r,while循环无法结束)
当mid=(l+r+1)/2,此时,mid = r,如果check为true,l = mid = r,此时while循环结束
代码示例,没有写main函数,只有大致模板
#include <iostream>
using namespace std;
bool check()
{
//根据特定题目来写
}
//寻找左半区符合性质的右边界点
int findRightBoundary(vector<int> nums, int target) {
int l = 0, r = nums.size() - 1;
while (l < r) {
int mid = (l + r + 1 )/ 2; //直接取平均可能会溢出,推荐 l + (( r - l + 1 ) >> 1)
if (check(nums[mid])) { // check函数用来判断当前mid位置是否符合性质
l = mid;
} else {
r = mid - 1;
}
}
return l;
}
//寻找右半区符合性质的左边界点
int findLeftBoundary(vector<int> nums, int target) {
int l = 0, r = nums.size() - 1;
while (l < r) {
int mid = (l + r )/ 2; // 向下取整
if (check(nums[mid])) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
例题:lAcWing 789. 数的范围 - AcWinghttps://www.acwing.com/problem/content/791/
P1678 烦恼的高考志愿 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1678
题目讲解 :CSDN博客https://blog.csdn.net/gege_0606/article/details/139043517?spm=1001.2014.3001.5502
二分答案
二分答案和整数二分的不同:二分答案枚举的是答案,整数枚举枚举的是下标。
P2440 木材加工 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P2440
例题 砍树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1873相应题解查看
二分答案练习题--砍树-CSDN博客https://blog.csdn.net/gege_0606/article/details/139044090?spm=1001.2014.3001.5501
STL 的二分查找(c++)
low_bound和upper_bound都要保证元素有序
low_bound
low_bound是返回一个迭代器,指向在范围内第一个不小于(大于或等于)给定值的元素。
如果没有找到,那么就返回末尾的迭代器。
语法: auto it = lower_bound(start, end, value)
以下代码返回的是不小于4 的下标
low_bound(v.begin(),v.end(),4)-v.begin();
upper_bound
upper_bound
返回一个迭代器,指向在范围内第一个大于给定值的元素。
如果没有找到,那么就返回末尾的迭代器。
语法: auto it = upper_bound(start, end, value)
用法,low_bound和up_bound可以用来快速计算某个值出现的次数,如果计算元素4在vector v出现的次数,代码如下:
count = upper_bound(v.begin(),v.end(),4) - low_bound(v.begin(),v.end(),4);
练习题P1102 A-B 数对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1102题解如下:二分练习题-CSDN博客https://blog.csdn.net/gege_0606/article/details/139043517?spm=1001.2014.3001.5502