二分1——2021-02-01更

二分1——二分查找(模板)

T1 二分查找(一)

题目:

输入一个整数n和n个整数,保证这n个整数已经按照从小到大进行排序。
然后输入一个整数q(q <= 100000)代表q次查询。
接下来n行,每一个数 x, 代表一次查询。
对于每次查询,判断该数 x 是否在之前输入的 n 个整数中出现过。如果出现,输出一行"Yes",否则输出"No"。

输入
第一行:一个整数n(n <= 100000)。
第二行:n个空格隔开的整数ai(1 <= ai <= 10^9)。
第三行:一个整数q。
接下来q行,每行包含一个整数x(1 <= x <= 10^9)。

输出
q行,每行为"Yes"或"No",表示对应的数是否存在。

输入样例
5
1 3 4 5 7
3
4
5
0

输出样例
Yes
Yes
No

思路:

这是一个二分的模板题,考的是最简单的一种二分。因为这个只是要查找这个数是否存在,所以只要在二分过程中如果找到这个数,那就可以直接break输出Yes,如果一直到二分结束都没有找到,那就输出No。

代码:

#include<iostream>
using namespace std;
int n,a[100010],x;

int main()
{
    
    
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	cin>>x;	
	for(int i=1;i<=x;i++)
	{
    
    
		int k;
		cin>>k;
		if(k<a[1]||k>a[n])
		{
    
    
			cout<<"No"<<endl;
			continue;
		}
		int z=1,y=n;
		bool v=false;
		while(z<=y)
		{
    
    
			int m=(z+y)/2;
			if(a[m]==k)
			{
    
    
				v=true;
				break;
			}
			else if(a[m]>k)
				y=m-1;
			else
				z=m+1;
		}
		if(v)
			cout<<"Yes"<<endl;
		else
			cout<<"No"<<endl;	
	}	
	return 0;
} 

T2 二分查找(二)

题目:

现有 n (n<=100000) 个已经按照从小到大进行排序的正整数(不超过10^9)。
然后输入一个整数q(q <= 100000)代表q次查询。
接下来有q个用空格隔开的数,每个数 x 代表一次查询。
对于每次查询,判断该数 x 是否在之前输入的 n 个数中出现过,如果出现则输出该数的序号,若有多个该数则输出最靠前的那个序号,如果没有出现过,则输出-1。

输入
第一行 2 个整数 n 和 m,表示数字个数和询问次数。
第二行 n 个整数,表示这些待查询的数字。
第三行 m 个整数,表示询问这些数字。

输出
一行m个用空格隔开的数,表示查询的结果。

输入样例
11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6

输出样例
1 2 -1

思路:

这道题目和T1不一样了,现在要求这个数如果存在要输出它最小的一个序号,也就是说,这个二分是一定要做到底的了,但是和刚才的思路不同,因为现在这道题目如果用回刚才的做法,那么会死循环,所以要用二分查找的另一个模板。

代码:

#include<iostream>
using namespace std;
int n,a[100010],b[100010],x;

int main()
{
    
    
	ios::sync_with_stdio(false);
	cin>>n>>x;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=x;i++)
		cin>>b[i];	
	for(int i=1;i<=x;i++)
	{
    
    
		int z=1,y=n;
		bool v=false;
		while(z<y)
		{
    
    
			int m=(z+y)/2;
			if(a[m]>=b[i])
				y=m;
			else if(a[m]<b[i])
				z=m+1;
		}
		if(a[z]==b[i])
			cout<<z<<" ";
		else
			cout<<-1<<" ";
	}	
	return 0;
} 

T3 查找最接近的元素

题目:

在一个非降序列中,查找与给定值最接近的元素。

输入
第一行包含一个整数n,为非降序列长度。1 ≤ n ≤ 100000。
第二行包含n个整数,为非降序列各元素。所有元素的大小均在0-1,000,000,000之间。
第三行包含一个整数m,为要询问的给定值个数。1 ≤ m ≤ 10000。
接下来m行,每行一个整数,为要询问最接近元素的给定值。所有给定值的大小均在0-1,000,000,000之间。

输出
m行,每行一个整数,为最接近相应给定值的元素值,保持输入顺序。若有多个值满足条件,输出最小的一个。

输入样例
3
2 5 8
2
10
5

输出样例
8
5

思路:

这个也是二分的一个模板,只要思路和前两题类似,只不过在做法上略有差别。

代码:

#include<iostream>
#include<cmath>
using namespace std;
int n,a[100010],b[100010],x;

int main()
{
    
    
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	cin>>x;
	for(int i=1;i<=x;i++)
		cin>>b[i];	
	for(int i=1;i<=x;i++)
	{
    
    
		int z=1,y=n,lz=n;
		while(z+1<y)
		{
    
    
			int m=(y-z)/2+z;
			if(a[m]==b[i])
			{
    
    
				y=m;
				z=m;
			}
			else if(a[m]>b[i])
				y=m;
			else
				z=m;
		}
		if(y==z)
			cout<<a[z]<<endl;
		else if(b[i]-a[z]<=a[y]-b[i])
			cout<<a[z]<<endl;
		else
			cout<<a[y]<<endl;		
	}	
	return 0;
} 

今日总结:

二分查找:每次都从数据范围的中间位置判断,如果数值小于寻找的目标数,那么将数据范围改成原来数据范围的后一半;如果大于目标数,则将数据范围缩小成前一半。
时间复杂度log2(n)

PS:二分查找时,数据一定要是有序的

附:初学二分,大佬们多多指教。

二分系列——二分2

猜你喜欢

转载自blog.csdn.net/SSL_wyd/article/details/113529522