二分
这篇文章给大家讲的是二分,不喜勿喷,谢谢。
二分是一个常见算法,通常用来做求最优解的问题。
我给大家看一个例题吧:
给定一个长度为\(n\)严格递增(即\(a_i>a_i\__1\))的数列\(a_i\),求数\(x\)在哪个位置。题目保证有解。
第一行输入\(n\),第二行输入数列\(a\), 第三行输入\(x\)。
\(1 \le n \le 100000\) \(1 \le a_i \le 5000000\)
乍一看是不是觉得会,直接暴力做法\(O(n)\)解决,以下是暴力代码
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int a[N],n,x;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
cin>>x;
int ans;
for(int i=1;i<=n;i++){
if(x==a[i]){
ans=i;
break;
}
}
cout<<ans<<endl;
return 0;
}
然而如果题目限制10ms,你就过不去了。这是我们采用二分。
二分使用条件:必须有单调性。这里的单调性在于题目强调了是严格递增。
说到底二分到底是什么,不要着急,马上就讲(前面基本是废话)
二分在每次查找是都会一半一半的找,以此题为例,先求出数列正中间值的位置\(mid\),从而求出中间的值,在于\(x\)作比较:
-
\(a[mid]==x\)则输出mid,并跳出循环。
-
\(a[mid]>x\),既然中间的值比\(x\)还大,中间往后的值也比\(x\)大,那么不管这一半,在前面一半继续查找。
-
\(a[mid]<x\),与上面的同理,既然中间的值比\(x\)还小,中间往前的值也比\(x\)小,那么不管这一半,在后面一半继续查找。
之后重复判断,知道\(a[mid]==x\)为止,这样的时间复杂度为\(O(log_2 n)\)比之前快了很多。
下面是代码实现:
#include <bits/stdc++.h>//万能头万岁
using namespace std;
int a[100005];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
}
int x;
scanf("%d",&x);
int l=1,r=n;//定义两个端点,用来求中间值的位置,和确定查找区域。
int ans=0;
while(l<=r)//l一定是<=r,能确保绝对能找到答案。
{
int mid=(l+r)/2;//求当前中间值位置
if(x==a[mid])//情况1
{
cout<<ans<<endl;
break;
}
else if(x<a[mid])//情况2
{
r=mid-1;//注意要-1,因为中间的值也不行
}
else if(x>a[mid])//情况3
{
l=mid+1;//注意要+1,因为中间的值也不行
}
}
return 0;
}
现在你认识二分了吗?
这只是二分其中一个模板,更多的就自己探索或观看其他文章吧!
如有不清楚或错误的地方大佬请指出。点个赞再走吧!谢谢!