P2249 【深基13.例1】查找

题目描述

输入 n 个不超过109 的单调不减的(就是后面的数字不小于前面的数字)非负整数 a1​,a2​,…,an​,然后进行m 次询问。对于每次询问,给出一个整数q,要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 −1 。

输入格式

第一行 2 个整数 n 和 m,表示数字个数和询问次数。

第二行 n 个整数,表示这些待查询的数字。

第三行 m 个整数,表示询问这些数字的编号,从 1 开始编号。

输出格式

输出一行,m 个整数,以空格隔开,表示答案。

输入输出样例

输入 #1

11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6

输出 #1

1 2 -1 

说明/提示

数据保证,1≤n≤10^6,0≤ai​,q≤10^9,1≤m≤10^5

本题输入输出量较大,请使用较快的 IO 方式。

-------------------------------------------------------华丽的分割线--------------------------------------------------------

一开始看到这题聪明的我立马写了一个for循环去遍历数组找出那个数。结果TLE()了。我以为是死循环,改了一下又给我wa()了。

”稻花香里说丰年,听取WA声一片。"宋代辛弃疾那个时间是就诞生了编程。所以,第一台电脑不在美国的宾夕法尼亚大学诞生,而是中国宋代辛弃疾发明的。果然,我就只代码猿中语文最好的

-------------------------------------------------------华丽的分割线--------------------------------------------------------

于是我从新整理心情,编出来了一种算法, 二分!!!

奶奶说到:“小伙,故事讲完了吗?”

好吧,上面都是编的,其实我一看这一题就能想到是二分我好聪明。毕竟它也是个模板题。

接下来我来先讲一讲二分。

-------------------------------------------------------华丽的分割线--------------------------------------------------------

首先,我演示一下从1 2 2 3 4 4 5 6 6中找出5的二分方法。

我们要二分得先要找出数组的中间的元素和下标。这时,我们要就用到L和R,分别表示左边界和右边界。那二分查找时L和R分别就是数组的开头下标1和结尾下标N。然后我们就要计算他的中间点。至于为什么要这样做,你看完就知道了。那中间点的常用词是mid,计算这个中间点记住这条公式,比其它要简单很多, mid=(l+r)/2。那在我给的这组样例里n就等于9。根据我们的公式得知mid等于5,那我们就判断下标为5的数大于等于还是小于我们要查找的那个数。

这时,下标为mid的数是4。那我们看一下4大于等于我们要查找的5还是小于我们要查找的5。结果是小于。这是,我们就知道了我们要找的数在mid的右边。因为序列是有序的,是从小到大的,所以mid右边的数都大于等于它。所以我们把L赋值为mid+1, 这是L的值为6,R的值位N,那说明我们要继续查找L到R之间那个数等于5。那这次,mid就等于(6+9)/2=7。

这时,我们要再判断一下、以mid为下标的数大于等于5还是小于5。我们要判断的这个a[mid]等于5,正好是答案,但是不要急,在二分算法里面一般是不会去专门判断答案的,同学们可以继续看下去,就知道为什么不用判断了。回到模拟,这个数5大于等于我们要找的答案(其实它就是答案)我们就把R赋值成mid,就是7。现在L=6, R=7。

然后我们继续用上面的方法。把mid赋值成(6+7)/2=6。然后下标为6的数是4,它小于我们要查找的数。于是我们把L赋值为6+1=7。

到这个时候,L和R都等于7了,不满足L<R的条件,到L<R已经不成立时,我就们需要停止执行以上步骤,也就是代码里的退出循环,因为我们每次都在执行同样的判断和同样的赋值。然后你再看,L等于什么,他就等于我们的答案。所以,二分完后L就是答案的下标。这时就有同学要问了,为什么不是R?当然,你在二分查找里面如果你while循环的条件是L<R并且L的赋值是mid+1就也可以输出R。但是在二分答案里面,会有些特殊的情况,后面我会将二分答案的。

好了二分查找讲完了,它每次都把区间分两半然后在其中一个有答案的区间里面继续找,所以这个算法叫二分。是不是理解了呢,没理解可以在评论区留言问我,我会尽量快的回答。

-------------------------------------------------------华丽的分割线--------------------------------------------------------

好了,我们回归题目再来看一下。emmmmm~,除了输入有些不一样,其它和我刚才讲的简直就是一摸一样,所现在以我们要思考的就是怎么把我刚刚说的文字转化成代码。

输入不用我多说,况且它本来就有序了,不然又要搞好多东西。然后我们边输入要查询的数字边进行二分查找。所以我把二分查找直接放函数find()里了。然后我用while循环,然后每次都判断L是不是小于R,是就继续循环,不是就退出。然后每次都判断a[mid]是否大于等于我们要查找的数。是就把R赋值为mid,否则把L赋值为mid+1,是不是和上面的一摸一样?!

接下来是撒花代码。 

-------------------------------------------------------华丽的分割线--------------------------------------------------------

#include <bits/stdc++.h>
using namespace std;
int n, m, tmp, a[1000005];
int find(int x) {
	int l=1, r=n;
	while(l<r){
		int mid=(l+r)/2;
		if(a[mid]>=x){
		    r=mid;
		}else{
		   l=mid+1; 
		} 
	}
	return (a[l]==x ? l : -1);
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i=1; i<=n; i++){
	    scanf("%d", &a[i]); 
    }
	for(int i=1; i<=m; i++){
		scanf("%d", &tmp);
		printf("%d ", find(tmp)); 
	}
	return 0;
}

本文章制作不易,希望大家能给我这个弱鸡博主点个赞和关注还有收藏,我将感激不尽。

看好了,这是给我点赞的所有粉丝。

 

 

 给我点赞的人我都点赞回去了,不信就试试吧!!!

谢谢大家!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值