1001:二分查找
题目描述
给定一个单调递增的整数序列,问某个整数是否在序列中。
输入
第一行为一个整数n (n不超过10000),表示序列中整数的个数;
第二行为n个整数;
第三行为一个整数m(m不超过50000),表示查询的个数;
接下来m行每行一个整数k。
输出
每个查询的输出占一行,如果k在序列中,输出Yes,否则输出No。
样例输入
5
1 3 4 7 11
3
3
6
9
样例输出
Yes
No
No
源代码1
代码1实际上并不是二分查找的做法,而是直接遍历数组中的所有数据进行查找,易于理解,但代码运行耗时会远大于二分查找算法的运行耗时,读者可自行验证两组代码耗时。(即使给定的整数序列无序,也能用下列代码实现。)
#include<stdio.h>
int main()
{
int n,num[10000],m,i,x[50000];
scanf("%d",&n);
for(i=1;i<=n-1;i++){
scanf("%d ",&num[i-1]);
}
if(i==n){
scanf("%d",&num[n-1]);
}
scanf("%d",&m);
for(i=1;i<=m;i++){
scanf("%d",&x[i-1]);
}
int j=1;
for(j=1;j<=m;j++){
int cnt=0;
for(i=1;i<=n;i++){
if(x[j-1]==num[i-1]){
printf("Yes\n");
cnt=1;
}
}
if(cnt==0){
printf("No\n");
}
}
return 0;
}
详解1
#include<stdio.h>
int main()
{
int n,num[10000],m,i,x[50000];//先定义一些后面要用到的参数,
//将原数字序列和要查找数字构成的序列分别定义为数组
scanf("%d",&n);//这里第一行输入整数n,而编入一条输入语句后,到下一句系统会自动换行
//这里无需再次换行
for(i=1;i<=n-1;i++){
scanf("%d ",&num[i-1]);
}
/*这里利用一个for循环语句将第二行要输入的数字依次输入,
两个数字间加一空格,考虑到最后一个数字输入后无需再输入空格,我们将该行最后一个数字输入单独列出*/
if(i==n){
scanf("%d",&num[n-1]);
}//这里利用一个if语句将该行最后数字输入单独列出
scanf("%d",&m);//接着输入数字m,再利用for循环和上面类似输入要查询的一系列数字
for(i=1;i<=m;i++){
scanf("%d",&x[i-1]);
}
int j=1;
for(j=1;j<=m;j++){//紧接着我们先在外块遍历要查询的数字
int cnt=0;
for(i=1;i<=n;i++){//对于每个要查询的数字,我们对它在原数字序列中做遍历
if(x[j-1]==num[i-1]){//如果要查询的数字是原序列中的数字,我们就输出Yes,
//如果没有,为了能让它输出No,我们定义一个判断符cnt,将它的初始值赋值为0,
//如果要查询的数字存在,cnt将会被更改为1,如果不存在,cnt还保持初始值0
printf("Yes\n");
cnt=1;
}
}
//最后我们判断初始值来决定要不要输出No
if(cnt==0){
printf("No\n");
}
}
return 0;
}
源代码2
代码2属于二分查找,它能够大大节省代码运行耗时
#include<stdio.h>
int main()
{
int n,x[10000],m,i,y[50000];
scanf("%d",&n);
for(i=1;i<=n-1;i++){
scanf("%d ",&x[i-1]);
}
if(i==n){
scanf("%d",&x[n-1]);
}
scanf("%d",&m);
for(i=1;i<=m;i++){
scanf("%d",&y[i-1]);
}
for(i=1;i<=m;i++){
int left=0;
int right=n+1;
while (left<=right) {
int mid = (left + right) / 2;
if (x[mid]<y[i-1]) {
left = mid + 1;
}else if(x[mid]>y[i-1]){
right = mid - 1;
}else if(x[mid]==y[i-1]){
printf("Yes\n");
break;
}
}
if (left>right){
printf("No\n");
}
}
return 0;
}
详解2
二分法,事实上是通过一次次的逻辑判断,不断缩小搜索范围,从而减少耗时。
二分法使用的前提:原序列是有序的,对数字来讲就是或递增或递减。
这里给出一个符合题干条件有序递增的简单数组来进行二分法原理的说明。
num[ ]={1,2,3,4,5,6,7,8,9}
先给出图解,希望读者能够通过图解对二分法有一个大体的了解
这个数组中第一个数num[0]=1,下标为0
最后一个数num[8]=9,下标为8
我们在求每一个对应数组的中间下标的时候直接用对应起始下标相加然后除以2就行,由于int作除会自动取整,我们不必担心下标为小数的问题。
由上述图解我们不难猜出,最终要找的数字对应数组中 数字所在下标时满足start==end
注意:这里不必纠结于数组总数是奇数还是偶数,也不必纠结于哪个是中间数。最终通过if语句比较判断要找的数字与剩余部分数组的中间值的大小都能将数组分为两段,然后取其中一段继续查询就能达到二分的目的。
#include<stdio.h>
int main()
{
//可别忘记题干的要求哦,我们要先输出一堆数据。
int n,x[10000],m,i,y[50000];
scanf("%d",&n);//这里输出数组中数字的个数n
for(i=1;i<=n-1;i++){
scanf("%d ",&x[i-1]);//这里使用for循环将n个数字依次输入,
//因为这n个数字在一行,我们将两个数字之间输入单个空格来隔开
//需要考虑的是当输入最后一个数字时,我们不必再输入一个空格
}
//于是这里用了一个if语句单独管控最后一个数字的输入
if(i==n){
scanf("%d",&x[n-1]);
}
scanf("%d",&m);
for(i=1;i<=m;i++){
scanf("%d",&y[i-1]);
}
for(i=1;i<=m;i++){//由于该题要查询的数字不止一个,我们采用遍历数组的方法挨个查询
int left=0;//这里定义下标的起点
int right=n+1;//这里定义下标的终点
while (left<=right) {//当左标小于右标时,我们就做上述图表中的操作
int mid = (left + right) / 2;//计算中间下标
if (x[mid]<y[i-1]) {//通过if判断比较要找的数与中间值的大小,
//来调整左右标,进而控制数组的取舍
left = mid + 1;
}else if(x[mid]>y[i-1]){
right = mid - 1;
}else if(x[mid]==y[i-1]){
printf("Yes\n");
break;//这里使用break语句,一旦找到查询值,立即退出循环,可减少循环次数,减少耗时
//读者可自行讨论如果无break语句它会增加哪些不必要的循环
}
}
if (left>right){//如果进行上述for循环,没有找到要查询的数,在上面的if语句的操控下,
//左标会大于右标,作为未查询到数字的判断条件来控制是否输出"No"
printf("No\n");
}
}
return 0;
}
1002:归并排序
题目描述
给定一个数列,用归并排序算法把它排成升序。
输入
第一行是一个整数n(n不大于10000),表示要排序的数的个数;
下面一行是用空格隔开的n个整数。
输出
输出排序后的数列,每个数字占一行。
样例输入
5
3 2 1 4 5
样例输出
1
2
3
4
5