二分

这篇文章给大家讲的是二分,不喜勿喷,谢谢。

二分是一个常见算法,通常用来做求最优解的问题。

我给大家看一个例题吧:

给定一个长度为\(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\)作比较:

  1. \(a[mid]==x\)则输出mid,并跳出循环。

  2. \(a[mid]>x\),既然中间的值比\(x\)还大,中间往后的值也比\(x\)大,那么不管这一半,在前面一半继续查找。

  3. \(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;
}

现在你认识二分了吗?

这只是二分其中一个模板,更多的就自己探索或观看其他文章吧!

如有不清楚或错误的地方大佬请指出。点个赞再走吧!谢谢!

posted @ 2023-10-15 15:04  Xu_dh  阅读(40)  评论(0)    收藏  举报