例2.10 查找学生信息 - 九度教程第18题
题目:
时间限制:1 秒 内存限制:32 兆 特殊判题:否
题目描述:
输入N个学生的信息,然后进行查询。
输入:
输入的第一行为N,即学生的个数(N<=1000)
接下来的N行包括N个学生的信息,信息格式如下:
01 李江 男 21
02 刘唐 男 23
03 张军 男 19
04 王娜 女 19
然后输入一个M(M<=10000),接下来会有M行,代表M次查询,每行输入一个学号,格式如下:
02
03
01
04
输出:
输出M行,每行包括一个对应于查询的学生的信息。
如果没有对应的学生信息,则输出“No Answer!”
样例输入:
4
01 李江 男 21
02 刘唐 男 23
03 张军 男 19
04 王娜 女 19
5
02
03
01
04
03
样例输出:
02 刘唐 男 23
03 张军 男 19
01 李江 男 21
04 王娜 女 19
03 张军 男 19
来源:
2003年清华大学计算机研究生机试真题
解析:
若依旧采用每次询问时线性遍历数组来查找是否存在我们需要查找的元素,那么,该算法的时间复杂度达到了O(n * m)(查找次数 * 每次查找所需比较的个数),而这已经达到了千万数量级。所以,另找方法来解决该题,没错,就是二分查找。
用二分查找查找长度为L的有序数组,时间复杂度可由原本线性查找的O(L)降低到O(logL)。
为了符合查找空间单调有序的要求,首先要对所有数组元素按照学号关键字升序排列。当数组内各元素已经升序有序时,就可以在每次询问某个特定学号的学生是否存在时,使用二分查找来查找该学生。
二分查找:
以一种带有策略性的、跳跃的方法来遍历查找空间~
1,前提:二分查找建立在待查找元素排列有序的前提上。
2,查找过程:
(1)将查找开始点设为数组第一个元素,结束点设为数组最后一个元素。
(2)中间点=(开始点+结束点)/2。将开始点、结束点正中间点的数与查找目标数字进行比较,
1)若该中间数字=查找目标数字,则查找成功,查找结束。
2)若中间数字>查找目标数字,移动结束点为中间数字的前一个数字。
3)若中间数字<查找目标数字,移动开始点为中间数字的后一个数字。
(3)若在查找过程中,出现查找起始点大于查找结束点的情况,则说明查找子集已经为空集,查找失败。
否则,继续步骤(2)的查找过程。
注:
1,for(int i=0;i<n;i++)
{
scanf("%s%s%s%d",buf[i].no,buf[i].name,buf[i].sex,&buf[i].age);
} //输入
1)这里输入字符串%s时没有用"&"符号~。解释:%s 是指输入一个字符串,而此处的buf[i].no即是一个字符串变量名,其本身就表示字符串buf[i].no的首地址,因此无须再加取地址符&。
%s格式符,此处表示用来输入一个字符串,而字符串是以数组的形式的存储的。c语言中数组名代表该数组的起始地址,此处buf[i].no为数组名,代表的是首地址,再用取地址符号,就重复了,请注意与%c的区别。
2)格式串里面也没有用空格~
char x[30];
scanf("%s",x); //待查找学号 这里输入字符串%s时没有用"&"符号~同上
2,九度教程第15题中,while(scanf("%d %c %c",&n,&a,&b)==3) //!!!注意此处格式串中必须含有空格~
3,二分查找的关键代码
代码:
#include<stdio.h>
#include<string.h> //strcmp函数头文件
#include<algorithm> //sort函数头文件
using namespace std;
struct Student{ //用于表示学生个体的结构体
char no[100]; //学号
char name[100]; //姓名
int age; //年龄
char sex[5]; //性别
bool operator < (const Student & A) const{
//重载小于运算符使其能用sort函数排序
return strcmp(no,A.no)<0;
}
}buf[1000];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<n;i++)
{
scanf("%s%s%s%d",buf[i].no,buf[i].name,buf[i].sex,&buf[i].age);
} //输入
sort(buf,buf+n); //对数组排序使其按照学号升序排列
int t;
scanf("%d",&t); //有t组询问
while(t--!=0) //while循环保证查询次数为t
{
int ans=-1; //目标元素下标,初始化为-1
char x[30];
scanf("%s",x); //待查找学号
int base=0,top=n-1; //初试时,开始下标0,结束下标n-1,查找子集为整个数组
while(top>=base) //当查找子集不为空集时重复二分查找
{
int mid=(base+top)/2; //计算中间点下表
int tmp=strcmp(buf[mid].no,x);//比较中间点学号与目标学号
if(tmp==0)
{
ans=mid;
break; //若相等,则查找完成跳出二分查找
}else if(tmp>0)//若大于,则结束下标变为中间点前一个点下标
{
top=mid-1;
}else{ //若小于,则开始点下标变为中间点后一个点下标
base=mid+1;
}
}
if(ans==-1) //若查找失败
{
printf("No Answer!\n");
}
else //若查找成功
{
printf("%s %s %s %d\n",buf[ans].no,buf[ans].name,buf[ans].sex,buf[ans].age);
}
}
}
return 0;
}