数据结构课程设计插队买票.docx
《数据结构课程设计插队买票.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计插队买票.docx(16页珍藏版)》请在冰豆网上搜索。
![数据结构课程设计插队买票.docx](https://file1.bdocx.com/fileroot1/2023-1/22/a7f1ac2c-920b-4903-b82f-da61612553df/a7f1ac2c-920b-4903-b82f-da61612553df1.gif)
数据结构课程设计插队买票
数据结构课程设计报告
插队买票
专业
学生姓名
班级
学号
指导教师
完成日期
2015年1月24日
数据结构课程的设计
1、设计题目
春节前夕,一年一度的运输高潮也开始了,成千上万的外出人员都往家赶.火车站售票窗前买票队伍一眼望不到头.运气好的,碰到一个已经在排队的朋友,直接走过去,排他后面,这就叫"插队",但对队伍里的其他人来说是不公平的.本课程设计的任务是写一个程序模拟这种情况.每个队伍都允许插队.如果你在排队,有一个以上的朋友要求插队,则你可以安排他们的次序.每次一个人入队,并且如果这个入队的人发现队伍中有自己的朋友,则可以插入到这个朋友的后面;当队伍中的朋友不止一个的时候,这个人会排在最后一个朋友的后面;如果队伍中没有朋友,则他只能够排在这个队伍的最后面.每一个入队的人都先进行上述的判断.当队伍前面的人买到车票之后.依次出队.
2、课题目的及要求
2.1目的:
数据结构课程设计是为数据结构课程独立开设的实践性教学环节。
数据结构课程设计对于巩固数据结构知识,加强学生的实际动手能力和提高学生综合素质是十分必要的。
本题目主要解决两个问题:
一是怎么存放和查找大量数据(主要是姓名);二是怎么操作“ENQUEUE”和“DEQUEUE”命令。
2.2要求:
1)输入要求:
程序从“input.txt”文件读入测试用例,一个文件可包含多个测试用例。
每个用例的第一行是朋友组的数目n(1<=n<=1000).对于一个朋友组以朋友的数目j(1<=j<=1000)开始,由朋友的个数以及他们的名字组成,一个空格后接该组朋友的名字,以空格分开,并且每个人的名字都不同。
每个名字不超过4个字母,由{A,B,…,Z,a,b,…,z}组成。
一个朋友组最多有1000个人,每个人只属于一个朋友组。
n=0时,测试数据结束。
下面是一些具体命令:
ENQUEUEX——X入队
DEQUEUE——排队头的人买票,离开队伍,即出队
STOP——一个测用例结束
2)、输出要求:
测试结果输出到“output.txt”文件中。
每个测试用例的第一行输出“Scenario#k”,k是测试用例的序号(从1开始)。
对每一个DEQUEUE命令,输出刚买票离开队伍的人名。
两个测试用例之间隔一空行,最后一个用例结束不输出空行。
3、设计分析
图1系统总图
3.1用散列表来存放和查找数据
由于最多有1000个朋友组,每组最多1000人,使用平方探测法解决冲突,则表的大小至少是2*(1000*1000),所以选择TableSize=2000003。
同一个组内的都是朋友,所以每个人除了记录他的名字name,还要记录他属于哪个朋友组group,另外用info来表示该单元是否被占用,数据结构如图2所示。
散列函数是根据Honer法则计算一个以64为阶的多项式,如图3所示。
冲突解决方法采用平方探测法,如图4所示。
#defineTabSize2000003
typedefstructhashtab*PtrToHash;
structhashtab
{
charname[5];
intgroup;
charinfo;/*用来表示该单元是否被占用*/
};
图2数据结构:
散列表
intHash(char*key,intTableSize)
{
intHashVal=0;
while(key!
=NULL)
HashVal=(HashVal<<6)+*key;
ReturnHashVal%TableSize;
}
图3散列函数
long int Find(PtrToHash hash,char *c)
{
key=c;
CurrentPos=Hash(key,TabSize); /*计算散列值*/
CollisionNum=0;
while((单元被占用)and(单元内的名字与查找的名字不同)) /*发生冲突*/
{ /*平方探测法*/
CurrentPos+=2*(++CollisionNum)-1;
if(CurrentPos>=TabSize)
CurrentPos-=TabSize;
}
return CurrentPos;/*返回在散列表中的位置*/
}
图4用平方探测法处理冲突
3.2怎么操作“ENQUEUE”和“DEQUEUE”命令
这可以用队列来模拟。
由于有插队现象的存在,不能单纯地用一个数组来表示队列,因为这样的话,插入一个朋友,则他后面的人都要往后移一个单位,删除一个人,则他后面的人都要前移一个,会降低效率。
所以,采用一个Index标记来表示当前元素的后继元素,最后一个单元的后继元素是第0个,形成环,数据结构如图5所示。
不用链表是因为链表存放指针也需要空间,并且链表插入、删除的效率没有数组高。
typedef struct Que *PtrToQue;
struct Que{
long int HashVal; /*散列值*/
long int Index; /*在中的队列序号*/
};
图5数据结构:
队列
1)输入ENQUEUE命令,如果队伍里有朋友,则排在朋友后面;如果没有遇到朋友,则排到队尾。
入队时,用一个数组记录每个朋友组的最后一位,以便下一个朋友到来时排到他后面,这个数组被称为“插入数组”。
2)输入“DEQUEUE”命令,则根据“先进先出”,按照各个元素和它后继元素的先后顺序,每次删除队列中的第一个。
程序结构如图6所示。
While(读测试文件)
{
if(输入“ENQUEUE”)
{
读入名字;
插入散列表;
插入队列;
}
else if(输入“DEQUEUE”)
{
删除队列第一个名字;
将该名字输出到文件;
}
Else stop;
}
图6入队、出队操作
4、调试与测试
4.1调试
先运行,出现如图7所示:
图7运行
4.2测试
1)测试输入
2
3AnnBobJoe
3ZoeJimFat
ENQUEUEAnn
ENQUEUEZoe
ENQUEUEBob
ENQUEUEJim
ENQUEUEJoe
ENQUEUEFat
DEQUEUE
DEQUEUE
DEQUEUE
DEQUEUE
DEQUEUE
DEQUEUE
STOP
2
5AnnyJackJeanBillJane
6EvaMikeRonSonyGeoZoro
ENQUEUEAnny
ENQUEUEEva
ENQUEUEJack
ENQUEUEJean
ENQUEUEBill
ENQUEUEJane
DEQUEUE
DEQUEUE
ENQUEUEMike
ENQUEUERon
DEQUEUE
DEQUEUE
DEQUEUE
DEQUEUE
STOP
0
2)正确输出
Scenario#1
Ann
Bob
Joe
Zoe
Jim
Fat
Scenario#2
Anny
Jack
Jean
Bill
Jane
Eva
4.3实际结果
1)实际输入
图8输入数据
2)实际输出
图9输出数据
5、小结
在前面的学习过程中我们学到了很多知识,而这次课程设计又是对我们所学的一次总结.我们必须掌握很多已学的知识才能很好的完成本次的课程设计。
在这次课程设计中,总的感觉是我遇到了很多困难,这主要是由于我编写代码的经验不足,有时虽然是一个很小的问题,但解决起来却花费了我不少的时间,值得欣慰的是,当自己苦思冥想或者和其它同学一起探讨,把问题解决的时候我还是觉得获益非浅,这就是在摸索中寻求到的知识。
设计期间,我发现只有有目的的去学习一些将要用到的东西,充分的运用所学知识,才能顺利的完成设计。
在设计时也免不了存在着一些不足,所以在今后的学习中我会努力取得更大的进步,对于我不足的地方希望老师能够及时给予批评,以便我在今后的学习中能够及时的改正。
通过这次课程设计,我收获的不仅仅是课程上的知识得到实际应用,还有编程的基本习惯和应注意的细节。
6、参考文献
[1]范策,周世平,胡哓琨.《算法与数据结构(C语言版)》[M].北京:
机械工业出版社,2004
[2]严蔚敏.《数据结构(C语言版)》.北京:
清华大学出版社,2004
[3]许卓群,杨冬青,唐世渭,张铭.《数据结构与算法》.北京:
高等教育出版社,2004
[4]徐孝凯.《数据结构实用教程(第二版)》.北京:
清华大学出版社,2006
7、源程序清单
#include
#include
#include
#defineTabSize2000003/*散列表大小TabSize是大于表最大空间的素数*/
#defineMax1000001/*队列空间最大值*/
structhashtab;
typedefstructhashtab*PtrToHash;
structhashtab/*散列表数据结构*/
{
charname[5];/*名字*/
intgroup;/*属于哪个朋友组*/
charinfo;/*标志位,该单元是否被占用*/
};
structQue;
typedefstructQue*PtrToQue;
structQue/*队列数据结构*/
{
longintHashVal;/*散列值*/
longintIndex;/*在中的队列序号*/
};
inthashedx=0;/*标记元素是否已经在散列表里*/
longintFind(PtrToHashhash,char*c)/*查找在散列表中的位置*/
{
char*key;
longintCurrentPos,CollisionNum;
key=c;
for(CurrentPos=0;*key;++key)/*散列函数,计算散列值*/
CurrentPos=(CurrentPos<<6)+*key;
CurrentPos%=TabSize;/*散列值*/
CollisionNum=0;
/*如果当前单元被占用:
单元内的元素与当前操作的名字不同,使用平方探测法解决冲突;与当前操作的名字相同,则直接返回在散列中的位置*/
while((hash[CurrentPos].info)&&(strcmp(hash[CurrentPos].name,c)))
{/*平方探测法*/
CurrentPos+=2*(++CollisionNum)-1;
if(CurrentPos>=TabSize)
CurrentPos-=TabSize;
}
if((hash[CurrentPos].info)&&(strcmp(hash[CurrentPos].name,c)==0))
/*元素已经在散列表里*/
hashedx=1;
else/*元素不在散列表里*/
hashedx=0;
returnCurrentPos;/*返回在散列表中的位置*/
}
intmain()
{
longintFind(PtrToHashhash,char*c);/*查找在散列表中的位置*/
PtrToHashhash;/*散列表*/
PtrToQuequeue;/*队列*/
int*grouppos;/*记录每个朋友组的最后一位,即插队数组*/
intn;/*测试用例数目*/
intnum;/*当前测试用例序号*/
longinti,ii,j,key,temp;
longinthead,last;/*队列的头和尾*/
charc[8],tempc[8];/*名字*/
FILE*fpin,*fpout;/*输入、输出文件指针*/
if(!
(fpin=fopen("input.txt","r")))/*打开测试文件*/
{
printf("fopenerror!
");/*文件打开错误*/
return-1;
}
if(!
(fpout=fopen("output.txt","w")))/*打开输出文件*/
{
printf("fopenerror!
");
return-1;
}
hash=(PtrToHash)malloc(sizeof(structhashtab)*TabSize);/*为散列表申请空间*/
queue=(PtrToQue)malloc(sizeof(structQue)*Max);/*为队列申请空间*/
grouppos=(int*)malloc(sizeof(int)*1000);/*申请空间记录每个朋友组的最后一位*/
for(i=0,j=1;iqueue[i].Index=j;
queue[i-1].Index=0;/*最后一个单元的后继单元是第0个,形成环*/
num=0;
for(fscanf(fpin,"%d",&n);n;fscanf(fpin,"%d",&n))/*输入当前测试用例的朋友组数*/
{
if(n<1||n>1000)/*处理异常输入n*/
{
fprintf(fpout,"nisoutofrange\n");
return-1;
}
num++;
if(num!
=1)/*两个测试用例间输入一空行*/
fprintf(fpout,"\n");
for(i=0;ihash[i++].info=0;/*初始化散列表,标记位置0*/
for(i=0;i{
fscanf(fpin,"%d",&j);/*当前组里的人数*/
if(j<1||j>1000)/*处理异常输入j*/
{
fprintf(fpout,"jisoutofrange\n");
return-1;
}
for(;j;--j)
{
fscanf(fpin,"%s",c);/*输入名字*/
for(ii=0;iitempc[ii]='\0';
strcpy(tempc,c);
ii=0;
while(tempc[ii]!
='\0')/*是否由四个以内字母组成*/
{
if(tempc[ii]<'A'||('Z''z'||ii>4)
{
fprintf(fpout,"Group%d:
Nonstandardname\n",i);
return-1;
}
ii++;
}
key=Find(hash,c);/*找到在散列表中的位置*/
if(hashedx==1)/*重名*/
{
fprintf(fpout,"repeatedname%s\n",c);
return-1;
}
strcpy(hash[key].name,c);/*插入散列表*/
hash[key].info=1;/*标记置1,该单元被占用*/
hash[key].group=i;/*记录他属于哪个组*/
}
}
for(i=0;i<1000;)
grouppos[i++]=0;/*初始化插队数组*/
head=0;/*初始化队列头、尾标记*/
last=0;
fprintf(fpout,"Scenario#%d\n",num);/*输出当前用例序号到文件*/
for(fscanf(fpin,"%s",c);;fscanf(fpin,"%s",c))/*输入命令*/
{
if(*c=='E')/*入队命令*/
{
fscanf(fpin,"%s",c);/*输入名字*/
key=Find(hash,c);/*查找在散列表中的位置*/
if(hashedx==0)/*散列表里没这个人*/
{
fprintf(fpout,"no%s\n",c);
return-1;
}
temp=queue[0].Index;/*队列第0个位置记录队尾的后继单元*/
queue[0].Index=queue[temp].Index;
/*在队列中申请一个新单元,队尾标记后移一个位置*/
queue[temp].HashVal=key;/*入队*/
if(!
head)/*如果是队列里的第一个元素*/
last=head=temp;/*队头、队尾标记指向第一个元素*/
if(!
grouppos[hash[key].group])/*如果队列里没朋友*/
{
queue[temp].Index=0;/*队尾指向对头,形成环*/
queue[last].Index=temp;/*前一次队尾的后继元素是当前元素*/
last=temp;/*队尾标记指向当前元素*/
grouppos[hash[key].group]=temp;
/*插队数组记录该朋友组里已入队的最后一位*/
}
else/*如果队列中已经有他的朋友*/
{
queue[temp].Index=queue[grouppos[hash[key].group]].Index;
/*插队到朋友的后面*/
queue[grouppos[hash[key].group]].Index=temp;
/*插队到朋友后面一位的前面*/
grouppos[hash[key].group]=temp;
/*替换插队数组里该组的元素为当前元素*/
if(hash[queue[last].HashVal].group==hash[key].group)
/*如果当前元素和前一元素是朋友,队尾标志指向当前元素*/
last=temp;
}
}
elseif(*c=='D')/*出队命令*/
{
if(last==0)/*不能对空队列执行出队命令*/
{
fprintf(fpout,"Emptyqueue!
\nCan'texecuteDEQUEUE!
\n");
return-1;
}
fprintf(fpout,"%s\n",hash[queue[head].HashVal].name);
/*输出队头元素到文件*/
temp=head;
head=queue[temp].Index;/*队列第一位出队,队头标记后移一位*/
queue[temp].Index=queue[0].Index;/*队列第0个元素后移一位*/
queue[0].Index=temp;/*释放空间*/
if(grouppos[hash[queue[temp].HashVal].group]==temp)
/*当前删除的元素是该朋友组在队列里的最后一位*/
grouppos[hash[queue[temp].HashVal].group]=0;
if(last==temp)/*出队后,队列为空*/
last=0;
}
else/*输入"STOP"*/
break;/*测试结束*/
}
}
fprintf(fpout,"\b");
fclose(fpin);
fclose(fpout);
return1;
}