学习视频:acwing算法基础课
思路:目标值在[ l , r ]中,每次将区间长度缩小一半,当l==r时,我们就找到了目标值
有单调性可以二分,可以二分不一定有单调性
整数二分
二分本质:
区间[ l , r ]中,存在某种性质,此性质半边区间满足,半边区间不满足,区间被一分为二。如果这样的性质存在,那么二分可以寻找这个性质的边界即点1和点2
情形一:寻找某性质的右边界(以上图为例,即点1)
第一步:寻找区间中间值mid=(l+r+1)/2,并判断中间值是否符合橙色区间的性质
mid=(l+r+1)/2中加1的原因:当l与r相邻时,如l=3,r=4,若mid=(l+r)/2=3,l向下取整仍然为3,没有改变,所以需要+1。
第二步:如果满足条件,则mid一定在左侧橙色区间内,mid一定小于等于点1,换言之点1一定在mid右侧或者就是mid,所以更新区间方式:l=mid
如果不满足条件,则mid一定在右侧绿色区域内,mid一定大于点1,换言之点1一定在mid左侧,所以更新方式r=mid-1
情形二:寻找某性质的左边界(以上图为例,即点2)
第一步:寻找区间中间值mid=(l+r)/2,并判断中间值是否符合绿色区间的性质
第二步:如果满足条件,则mid一定在右侧绿色区间内,mid一定大于等于点2,换言之点2一定在mid左侧或者就是mid,所以更新区间方式:r=mid
如果不满足条件,则mid一定在左侧橙色区域内,mid一定小于点2,换言之点2一定在mid右侧,所以更新方式l=mid+1
举例:给出一个数组1 2 2 3 3 3 5 9 ,找出其中3的起始和终止下标
#include<iostream>
using namespace std;
const int N = 100005;//数组最大元素个数
int n, q[N], k;//n:数组元素个数,k带查找端点下标的数字
int main()
{
cin >> n >> k;
for (int i = 0; i < n; i++)
{
cin >> q[i];
}
int l = 0, r = n - 1;
//起始坐标
while (l < r)
{
int mid = (l + r) / 2;
if (q[mid] >= k) r = k;
else l = mid + 1;
}
cout << l << " ";
l = 0, r = n - 1;
//终止坐标
while (l < r)
{
int mid = (l + r + 1) / 2;
if (q[mid] <= k) l = mid;
else r = mid - 1;
}
cout << r;
return 0;
}
浮点数二分
与整数二分相似在区间l到r上找一个性质使半边区间符合,半边区间不符合,当区间长度很小时就可以认为是找出边界啦。
举个例子:找出数字3的平方根
#include<iostream>
using namespace std;
const int N = 100005;//数组最大元素个数
int n, q[N], k;//n:数组元素个数,k带查找端点下标的数字
int main()
{
double x=3;
double l = 0, r = x;
while (r - l > 1e-8)//当r-l大于1的-8次方时就继续寻找
{
double mid = (l + r) / 2;
if (mid * mid >= x) r = mid;//mid平方大于等于x时说明mid大于等于x的平方根所以区间调整为l到mid
else l = mid;//mid平方小于x时说明mid小于x的平方根所以区间调整为mid到r
}
cout << l;
return 0;
}