数据结构课程设计题目总Word文档下载推荐.docx
《数据结构课程设计题目总Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计题目总Word文档下载推荐.docx(14页珍藏版)》请在冰豆网上搜索。
带注释的源程序。
如果提交源代码,可以在实验报告列出程序文件名的清单。
●实验报告可以以电子稿提交。
验收:
抽签决定顺序。
1月19,20号两天。
早上9:
00-晚上9:
00,一个小时一个组。
[题目]
一、离散事件模拟
①银行业务模拟
【问题描述】客户业务分为两种,第一种是申请从银行得到一笔资金,即取款或者借款。
第二种是向银行投入一笔资金,即存款或者还款。
银行有两个服务窗口,相应的有两个队列。
客户到达银行后先排第一个队。
处理每个客户业务时,如果属于第一种,且申请额超出银行现存资金总额而得不到满足,则立即排入第二个队等候,直至满足时才离开银行;
否则业务处理完后立即离开银行。
每接待完一个第二种业务的客户,则顺序检查和处理(如果可能)第二个队列中的客户,对能满足的申请者予以满足,不能满足的者重新排到第二个队列的队尾。
注意:
在此检查过程中,一旦银行的资金总额少于或者等于刚才第一个队列中最后一个客户(第二种业务)被接待之前的数额,或者本次已将第二个队列检查或处理了一遍,就停止检查(因为此时已不可能还有能满足者)转而继续接待第一个队列客户。
任何时刻都只开一个窗口。
假设检查不需要时间。
营业时间结束时所有客户立即离开银行。
【基本要求】利用动态存储结构实现模拟。
【测试数据】一天营业开始时银行拥有的款额为10000(元),营业时间为600(分钟)。
其他模拟参量自定,注意测定两种极端的情况:
一是两个到达事件之间的间隔时间很短,而客户的交易时间很长,另一个恰好相反,设置两个到达事件的间隔时间很长,而客户的交易时间很短。
【实现提示】事件有两类:
到达银行的和离开银行。
初始时银行现存资金总额为total。
开始营业后的第一个事件是客户到达,营业时间从0到closetime。
到达事件发生时随机地设置此客户的交易时间和距下一到达事件之间的时间间隔。
每一个客户要办理的款额也是随机确定的,用负值和正值分别表示第一类和第二类业务。
变量total,closetime以及上述两个随机量的上下界均交互地从终端读入,作为模拟参数。
两个队列和一个事件表均要用动态存储结构实现。
注意弄清应该在什么条件下设置离开事件,以及第二个队列用怎么样的存储结构实现时可以获得较高的效率。
事件表是按时间顺序有序的。
②航空客运订票系统
【问题描述】航空客运订票的业务活动包括:
查询航线、客票预订和办理退票等。
试设计一个航空客运订票系统,以使上述业务可以借助计算机来完成。
【基本要求】
(1)每条航线所涉及的信息有:
终点站名、航班号、飞机号、飞行周日(星期几)、乘员定额、余票量、已订票的客户名单(包括姓名、订票量、舱位等级1,2或3)以及等候替补的客户名单(包括姓名、所需的票量);
(2)作为示意系统,全部数据可以只放在内存中;
(3)系统能实现的操作和功能如下:
a-查询航线:
根据旅客提出的终点站名输出下列信息:
航班号、飞机号、星期几飞行、最近一天航班的日期和余票;
b-承办订票业务:
根据客户提出的要求(航班号、订票数额)查询该航班票额情况,若尚有余票,则为客户办理订票手续,输出座位号;
若已满员或余票额少于订票额,则需重新询问客户要求。
若需要,可登记排队候补;
c-承办退票业务:
根据客户提供的情况(日期、航班),为客户办理退票手续,后人内查询该航班时候有人排队候补,首先询问排在第一的客户,若所退票额能满足他的要求,则为他办理订票手续,否则依次询问其他排队候补的客户。
【测试数据】自行拟定。
【实现提示】两个客户名单可分别由线性表和队列实现。
为查找方便,已订票客户的线性表应按客户姓名有序,并且,为插入和删除方便,应以链表作存储结构。
由于预约人数无法预计,队列也应以链表作存储结构。
整个系统需汇总各条航线的情况登录在一张线性表上,由于航线基本不变,可采用顺序存储结构,并按航班有序或按终点站名有序。
每条航线是这张表上的一个记录,包含上述8个域,其中乘员名单域为指向乘员名单链表的头指针,等候替补的客户名单域为分别指向队头和队尾的指针。
【选作内容】当客户订票要求不能满足时,系统可向客户提供到达同一目的地的其他航线的情况。
读者还可以充分发挥自己的想象力,增加你的系统的功能和其他服务项目。
③电梯模拟
【问题描述】设计一个电梯模拟系统。
这是一个离散的模拟程序,因为电梯系统是乘客和电梯等“活动体”构成的集合。
虽然他们彼此交互作用,但是他们的行为是基本独立的。
在离散的模拟中,以模拟时钟决定每个活动体的动作发生的时刻和顺序,系统在某个模拟瞬间处理有待完成的各种事情,然后把模拟时钟推进到某个动作预定要发生的下一个时刻。
(1)模拟某校5层教学楼电梯系统。
该楼有个自动电梯,能在每一层停留。
5个楼曾由下至上依次称为地下层,第一层,第二层,第三层和第四层,其中第一层是大楼的进出层,即是电梯的“本垒层”,电梯“空闲”时,将来到该层待命。
(2)乘客可随机地进出于任何层。
对每个人来说,他有一个能容忍的最长等待时间,一旦等候电梯时间过长,他将放弃。
(3)模拟时钟从0开始,时间单位为0.1秒。
人和电梯各种动作均要耗费一定时间单位(记为t),如:
有人进出时,电梯每隔40t测试一次,若无人进出,则关门;
关门和开门各需20t;
每个人进出电梯均需要25t;
如果电梯在某层静止时间超过300t,则驶回1层待命。
(4)按时序显示系统状态的变化过程:
发生的全部人和电梯的动作序列。
【测试数据】模拟时钟Time的初始值为0,终值可在500~10000范围内逐步增加。
【实现提示】
(1)楼层由上自下依次编号为0,1,2,3,4。
每层有要求Up(上)和Down(下)的两个按钮,对应10个变量CallUp[0,4]和CallDown[0..4]。
电梯内5个目标层按钮对应变量CallCar[0..4]。
有人按下某个按钮时,相应的变量就置为1,一旦要求满足后,电梯就把该变量清为0。
(2)电梯处于三种状态之一:
Going(上行)、GoingDown(下行)和Idle(停候)。
如果电梯处于Idle状态且不在1层,则关门并驶回1层。
在1层停留时,电梯是闭门候命。
一旦收到往另一层的命令,就转入GoingUp或GoingDown状态,执行相应的操作。
(3)用变量Time表示模拟时钟,初值为0,时间单位(t)为0.1秒。
其他重要的变量有:
Floor---电梯的当前位置(楼层);
D1---值为0,除非人们正在进入和离开电梯;
D2---值为0,如果电梯已经在某层停候300t以上;
D3---值为0,除非电梯门正开着又无人进出电梯;
State---电梯的当前状态(GoingUp,GoingDown,Idle)。
系统初始时,Floor=1,D1=D2=D3=0,State=Idle。
(4)每个人从进入系统到离开称为该人在系统中的存在周期。
在此周期内,他有6种可能发生的动作:
M1.[进入系统,为下一人的出现作准备]产生以下数值:
InFloor---该人进入哪层楼;
OutFloor---他要去哪层楼;
GiveupTime---他能容忍的等候时间;
InterTime---下一人出现的时间间隔,据此系统预置下一人进入系统的时刻。
M2.[按电钮并等候]此时应对以下不同情况作不同的处理:
1Floor==InFloor且电梯的下一个活动是E6(电梯在本层,但正在关门)
2Floor==InFloor且D3≠0(电梯在本层,正有人进出)
3其他情况,可能D2=0或电梯处于活动E1(在1层停候)。
M3.[进入排队]在等候队列Queue[InFloor]末尾插入该人,并预置在GiveupTime
个t之后,他若仍在队列中将实施动作M4。
M4.[放弃]如果Floor≠InFloor或D1=0,则从Queue[InFloor]和系统删除该人。
如果Floor=InFloor且D1≠0,他就继续等候(他知道马上就可以进入电梯)
M5.[进入电梯]从Queue[InFloor]删除该人,并把他插入到Elevator(电梯)栈中。
置CallCar[OutFloor]为1。
(5)电梯的活动有9种:
E1.[在1层停候]若有人按下一个按钮,则调用Controler将电梯转入活动E3或E6。
E2.[要改变状态?
]如果电梯处于GoingUp(或GoingDown)状态,但该方向的楼层却无人等候,则要看反方向楼层是否有人等候,而决定置State为GoingDown(或GoingUp)还是Idle。
E3.[开门]置D1和D2为非0值,预置300个t后启动活动E9和76个t后启动E5,然后预置20个t后转到E4。
E4.[让人出入]如果Elevator不空且有人的OutFloor=Floor,则按进入的倒序每隔25个t让这类人立即转到他们的动作M6。
Elevator中不再有要离开的人时,如果Queue[Floor]不空,则以25个t的速度让他们依次转到M5。
Queue[Floor]空时,置D1为0,
D3≠0,而且等候某个其他活动的到来。
E5.[关门]每隔40个t检查D1,直到是D1=0(若D1≠0,则仍有人出入)。
置D3为0并预置电梯再20个t后启动活动E6(再关门期间,若有人到来,则如M2所述,门再次打开)。
E6.[准备移动]置CallCar[Floor]为0,而且若State≠GoingDown,则置CallUp[Floor]为0;
若State≠GoingUp,则置CallDown[Floor]为0。
调用Controler函数。
如果State=Idle,则即使执行了controler,也转到E1。
否则,如果D2≠0,则取消电梯活动E9。
最后,如果State=GoingUp,则预置15个t后(电梯加速)转到E7;
如果State=GoingDown,则预置15个t后(电梯加速)转到E8。
E7.[上升一层]置Floor加1并等候51个t。
如果现在CallCar[Floor]=1或CallUp[Floor]=1,或者如果((Floor=1或CallDown[Floor]=1)且CallUp[j]=CallDown[j]=CallCar[j]=0对于所有j>
Floor),则预置14个t后(减速)转到E2;
否则重复E7。
E8.[下降一层]除了方向相反之外,与E7类似,但那里的51和14个t,此时分别为61和23个t(电梯下降比上升慢)。
E9.[置不活动指示器]置D2为0并调用Controler函数(E9是由E3预置的,但几乎总是被E6取消了)。
(6)当电梯须对下一个方向作出判定时,便在若干临界时刻调用Controler函数。
该函数有以下要点:
C1.[需要判断?
]若State≠Idle,则返回。
C2.[应该开门?
]如果电梯处于E1且CallUp[1],CallDown[1]或CallCar[1]非0,则预置20个t后启动E3,并返回。
C3.[有按钮按下?
]找最小的j≠Floor,使得CallUp[j],CallDown[j]或CallCar[j]非0,并转到C4。
但如果不存在这样的j,那么,如果Controler正为E6所调用,则置j为,否则返回。
C4.[置State]如果Floor>
j,则置State为GoingDown;
如果Floor<
j,则置State为GoingUp。
C5.[电梯静止?
]如果电梯处于E1而且j≠1,则预置20个t后启动E6,返回。
(7)由上可见,关键是按时序管理系统中所有乘客和电梯的动作设计合适的数据结构。
【选作内容】
(1)增加电梯数量,模拟多梯系统。
(2)某小区有一座30住宅楼,有三部自动电梯,每梯最多载客15人。
大楼每层8户,每户平均3.5人,每天早晨平均每户有3人必须在7点之间离开大楼去上学或者上班。
模拟该电梯系统,并分析分别在一梯,二梯和三梯运行情况下,下楼高峰期间各层的住户应提前多少时间候梯下楼?
研究多梯运行最佳策略。
二、排序算法比较研究(基本题目)
【问题描述】在教科书中,各种排序算法的时间复杂度分析结果只给出了算法执行时间的渐进度,或者大概执行时间。
试通过随机数据比较算法的关键字比较次数和关键字移动次数,以取得直观的感受。
(1)对各种内部排序算法进行比较,包括(书中没有提到请自行查阅课外资料):
冒泡排序,插入排序,选择排序,计数排序,快速排序,归并排序,希尔排序,基数排序,堆排序。
(2)待排序表的表长不小于100;
其中数据要用伪随机数产生程序产生;
至少要用5组不同的输入数据作比较;
比较的指标有关键字参加的比较次数和关键字的移动次数。
(关键字交换计为3次移动)
(3)最后要对结果作出简单的分析,包括对各组数据得出结果波动大小的解释。
【实现提示】主要工作是设法在已知算法中的适当位置插入对关键字的比较次数和移动次数的计数操作。
程序还可以考虑几组数据的典型性,如:
正序,逆序和不同程度的乱序。
注意采用分块调试的方法。
【选作内容】对不同的输入表长作试验,观察检查两个指标相对于表长的变化关系。
还可以对稳定性作验证。
三、小型数据库管理(模拟)系统:
通讯录系统//或者:
人员(学生)管理系统
要求建立一个简洁完备的通讯录,内容可以保存在内存中也可以保存在磁盘中(但要维持数据的一致性)。
要求能够实现(插入,删除,搜索,排序,外部存储:
保证数据动态维护与一致性)等功能。
①
(1)选用线性表(静态表/动态表Arrayorlinkedlist:
单向链表/双向链表/双向循环链表)
②
(2)非线性结构实现:
二叉搜索树(BinarySearchTree)
③(3)非线性结构实现:
AVL树
④(4)非线性结构实现:
哈希表(散列)Hashtable
问题要求:
设计记录数据,选一种结构实现应用系统[其中2和3还可以考虑是否使用穿线(Threaded)]。
说明选用该种结构的理由:
效率与你认为的优点。
分析存在的问题。
要求讨论关键字重复情况下的解决情况。
要求信息保存准确,界面友好,操作相对比较方便。
四、压缩器/解压器
【问题描述】
为了节省存储空间,常常需要把文本文件采用压缩编码的方式储存。
例如:
一个包含1000个x的字符串和2000个y的字符串的文本文件在不压缩时占用的空间为3002字节(每个x或每个y占用一个字节,两个字节用来表示串的结尾)。
同样是这个文件,采用游程长度编码(run-lengthcoding),可以存储为字符串1000x2000y,仅为10个字母,占用12个字节。
若采用二进制表示游程长度(1000和2000)可以进一步节约空间。
如果每个游程长度占用2个字节,则可以表示的最大游程长度为2*pow(16),这样,上例中的字符串只需要用8个字节来存储。
当要读取编码文件时,需要对其进行解码。
由压缩器(compressor)对文件进行编码,由解压器(decompressor)进行解码。
①
(1)长度-游程编码的压缩/解压;
+
(2)LZW压缩/截压(散列);
②
(1)长度-游程编码的压缩/解压;
+(3)霍夫曼编码压缩/解压(霍夫曼树)
要求选用二种压缩/解压策略实现压缩/解压器[
(1)为必选]。
输入的为本文文件(.txt),输出的为一种自定义的文件(.nz)。
考虑当构成文本的字符集合为{a,b,c,……,z,0,1,2,…9}时,请用实例测试你的压缩/解压器。
你的压缩器会不会出现抖动?
(压缩后的文本比原来的还要大)。
扩充构成文本的字符集合以便使它适应更一般的情况。
LZW:
由Lempel、Ziv和Welch这三位科学家所开发的技术。
该方法把文本的字符串映射为编码,首先,为该文本中所有可能出现的字母分别分配一个代码。
要压缩的对象是aaabbbbbbbaabaaba,由a和b组成。
为a分配代码0,为b分配代码1。
字符串和编码的关系被存储在字典中。
字典如下:
Key
1
2
3
4
5
6
7
Code
A
b
Aa
aab
bb
bbb
bbba
aaba
LZW压缩器不断的在输入文件中寻找在字典中出现的最长的前缀p,并输出其相应的代码。
若输入文件的下一个字符为c,则为pc分配下一个代码,并插入字典,这种策略称为LZW规则。
相反,在解压时,编码表由压缩文件重新构造,LZW原则使这种重建成为可能。
如上例子,压缩时,文件中第一个在字典中出现的最长前缀是a,输出其编码0,然后为字符串aa分配代码2,并插入到字典中。
余下的字符串在字典中出现的最长前缀是aa,输出aa的对应代码2,同时为字符串aab分配代码3并将其插入到字典中。
依次类推,由此,输出0214537
解压时,要输入代码,然后用代码所表示的文本来替换这些代码。
代码到文本的映射可按下面的方法重建:
首先把分配给单一字母的代码插入到字典中。
象前面一样,字典的入口为key-code对。
然而此时是根据给定的代码(key)去寻找相应的入口(而不是根据文本Code)。
压缩文件中的第一个代码对应于单一的字母,因此可以由该字母代替。
对于压缩文件中的其他代码p,要考虑两种情况:
1)在字典中;
2)不在字典中。
在1)情况下,找到p对应的文本text(p)输出。
并且,根据压缩原理可知,若在压缩文件中代码q写在p之前且text(q)是与q对应的文本,则压缩器会为文本text(q)(其后紧跟fc(p),text(p)的第一个字符)分配一新代码。
因此在字典中插入序偶(下一个代码,text(q)fc(p))。
情况2)时,只有在当前文本段形如text(q)text(q)fc(q)且text(p)=text(q)fc(q)时才会发生。
相应的压缩文件段是qp。
在压缩过程中,为text(q)fc(q)分配的代码为p。
在解压过程中,在用text(q)代替q后,又遇到代码p。
然而,此时字典中没有与p对应的文本。
因为这种情况只在解压文本段为text(p)text(q)fc(q)时才发生,因此可以对p解码。
当遇到一个没有定义代码文本对的代码p时,p对应的文本为text(q)fc(q),其中q为p前面的代码。
如上例子:
首先,初始化字典,在其中插入(0,a),(1,b)。
压缩的第一个代码为0,则用a代替之。
下一个代码2未定义,因为前一个代码为0,且text(0)=a,fc(0)=a,则
text
(2)=text(0)fc(0)=aa。
因此用aa代替2,并把(2,aa)插入字典中。
下个代码1由b来替换,并把(3,text
(2)fc
(1))=(3,aab)插入字典中。
依次类推,得解压结果。
霍夫曼编码:
根据不同符号在文本中出现的不同的频率来进行压缩编码。
假设文本是由a,u,x,z组成的字符串,若这个字符串的长度为1000,每个字符用一个字节来存储,共需1000个字节(即8000位)的空间。
如果每个字符用2位二进制来编码(00=a,01=x,10=u,11=z),则用2000位二进制即可以表示1000个字符。
此外,还需要一定的空间来存放编码表,可以采用如下格式来存储:
符号个数:
代码1,符号1,代码2,符号2,……
符号个数及每个符号分别用8位二进制来表示,每个代码需要占用[log2(符号个数)]位二进制。
因此,上例中,代码表需占用5*8+4*2=48位,压缩比为8000/2048=3.9。
利用这种编码方法,字符串aaxuaxz的压缩码为二进制串00000110000111,每个字符的编码具有相同的位数(两位)。
从左到右依次从位串中取出两位,通过查编码表边可以获得原字符串,这是解压缩过程。
我们利用霍夫曼编码来实现压缩,必须:
1)必须获得不同字符的频率。
2)建立具有最小加权外部路径的二叉数(即霍夫曼树),树的外部结点用字符串中
的字符表示,外部结点的权重(weight)即为该字符出现的频率。
3)遍历从根到外部结点的路径得到每个字符的编码。
4)使用字符的编码来代替字符串中的字符。
为了方便解码,需要保存字符代码映射表或每个字符的
频率表(在保存信息为频率表的情况下,解码需要重构霍夫曼数以获得相应的编码表)。
构造霍夫曼树:
首先从仅含一个外部结点的二叉树集合开始,每个外部结点代表字符串的一个不同的字符,其权重等于该字符的频率。
此后不断的从集合中选择两棵具有最小权重的二叉树,并把它们合并成一棵新的二叉树,合并方法是把这两棵二叉树分别作为左右子树,然后增加一个新的根结点。
新二叉树的权重为两棵子树的权重之和。
这个过程一直可以持续到仅剩下一棵树为止。
[二叉树的集合可以使用有序表/优先队列(基于队列/基于堆)实现]。
编码:
构造完毕霍夫曼树后,可以对从根开始到外部结点(叶子)的路径进行编码,方法是向左孩子移动时取0,向右孩子移动时取1。
【选作内容】对于策略2)我们用这种方法修改它:
每当压缩/解压1024*x个字节后,重新初始化代码表。
取文本长度为100K到200K之间,x=10,20,30,40和50。
测试修改后的程序,请给出你的结论:
采用那种x值比较好?
对于霍夫曼编码:
当文本中的字符出现的频率差别很大时,我们可以通过使用变长的编码来降低每个位串的长度。
但是,怎样对使用变长编码的位串解码呢?
我们可以发现:
在得到的霍夫曼编码中,没有任何一个代码是另一个代码的前缀。
因此与编码向匹配的实际的字符是唯一的。
请用实现这样的变长策略,并验证它。
五、人工智能中的搜索策略
1全国交通咨询模拟
【问题描述】处于对不同目的的旅客对交通工具有不同的要求。
例如,因公出差的旅客希望在旅途中的时间尽可能短,出门旅游的游客则希望旅费尽可能省,而老年旅客则要求中转次数最少。
编制一个全国城市间的交通咨询程序,为旅客提供两种或三种最优决策的交通咨询。
(1)提供对城市信息进行编辑(如:
添加或删除)的功能。
(2)城市之间有两种交通工具:
火车和飞机。
提供对列车