《数据结构》实验指导书.docx
- 文档编号:12479053
- 上传时间:2023-04-19
- 格式:DOCX
- 页数:67
- 大小:386.95KB
《数据结构》实验指导书.docx
《《数据结构》实验指导书.docx》由会员分享,可在线阅读,更多相关《《数据结构》实验指导书.docx(67页珍藏版)》请在冰豆网上搜索。
《数据结构》实验指导书
数据结构
实
验
指
导
书
东华大学计算机科学与技术学院
2009年9月
目录
前言1
一、概述1
二、实验步骤2
三、实验报告规范5
四、算法书写规范6
五、参考书目8
实验〇顺序表与链表10
实验〇实验报告示例12
实验一线性表25
实验报告示例:
集合的并、交和差运算28
实验二栈和队列42
实验报告示例:
迷宫问题45
实验三树和二叉树57
实验四图60
实验五查找63
实验六排序65
实验七综合设计性实验——航班信息的查询与检索67
前言
一、概述
上机实验是对学生的一种全面综合训练,是与课堂听讲、自学和练习相辅相成的必不可少的一个教学环节。
通常,实验题中的问题比平时的习题复杂得多,也更接近实际。
实验着眼于原理与应用的结合点,使读者学会如何把书上学到的知识用于解决实际问题,培养软件工作所需要的动手能力;另一方面,能使书上的知识变“活”,起到深化理解和灵活掌握教学内容的目的。
平时的练习较偏重于如何编写功能单一的“小”算法,而实验题是软件设计的综合训练,包括问题分析、总体结构设计、用户界面设计、程序设计基本技能和技巧,多人合作,以至一整套软件工作规范的训练和科学作风的培养。
此外,还有很重要的一点是:
机器是比任何教师都严厉的检查者。
为了达到上述目的,本指导书安排了七个主实验单元(其中除实验0作为预备练习,训练数组和链表的编程方法和编程技巧),其它各单元的训练重点在于基本的数据结构,而不强调面面俱到。
各实验单元与教科书的各章只具有粗略的对应关系,一个实验题常常涉及几部分教学内容。
在每个实验单元中安排有难度不等的实验题,经验表明,如果某题的难度略高于自己过去所对付过的最难题目的难度,则选择此题能够带来最大的收益。
切忌过分追求难题或者容易的题目。
一些实验题采取了统一的格式(其余的实验题由于比较简单,没有这么详细列出来,读者可举一反三),由问题描述、基本要求、测试数据、实现提示和选做内容五个部分组成。
问题描述旨在为读者建立间题提出的背景环境,指明问题“是什么”,基本要求则对问题进一步求精划出问题的边界,指出具体的参量或前提条件,并规定该题的最低限度要求,测试数据部分旨在为检查学生上机作业提供方便,在完成实验题时应自己设计完整和严格的侧试方案,当数据输入量较大时,提倡以文件形式向程序提供输入数据。
在实现提示部分,对实现中的难点及其解法思路等问题作了简要提示.选做部分向那些尚有余力的读者提出了更严峻的挑战,同时也能开拓其他读者的思路,在完成基本要求时力求避免就事论事的不良思想方法,尽可能寻求具有普遍意义的解法,使得程序结构合理,容易修改扩充。
不难发现,这里与传统的做法不同,题目设计得非常详细。
会不会限制读者的想象力、影响创造力的培养呢?
回答是:
软件发展的一条历史经验就是要限制程序设计者在某些方面的创造性,从而使其创造能力集中地用到特别需要创造性的环节之上。
实验题目本身就给出了问题说明和问题分解求精的范例,使读者在无形中学会模仿,它起到把读者的思路引上正轨的作用,避免坏结构程序和坏习惯,同时也传授了系统划分方法和程序设计的一些具体技术,保证实现预定的训练意图,使某些难点和重点不会被绕过去,而且也便于教学检查。
题目的设计策略是:
一方面使其难度和工作量都较大,另一方面给读者提供的辅助和可以模仿的成份也较多。
当然还应指出的是,提示的实现方法未必是最好的,读者不应拘泥于此,而应努力开发更好的方法和结构。
本实验指导书的一个特点是为实验制定了严格的规范〔见后面各章节)。
一种普遍存在的错误观念是,调试程序全凭运气。
学生花两个小时的上机时间只找出一个错误,甚至一无所获的情况是常见的。
其原因在于,很多人只认识到找错误,而没有认识到努力预先避免错误的重要性,也不知道应该如何努力。
实际上,结构不好、思路和概念不清晰的程序可能是根本无法调试正确的。
严格按照实验步骤规范进行实验不但能有效地避免上述种种问题,更重要的是有利于培养软件工作者不可缺少的科学工作方法和作风。
前三个实验单元提供了一个完整的实验报告示例,在起到实验报告规格范例作用的同时,还隐含地提供了很多有益的东西,比如,基于数据类型的系统划分方法;递归算法设计方法和技巧;对于有天然递归属性的问题如何构造非递归算法;以及所提倡的程序设计风格等。
但从另一方面看,计算机学科在不断发展,可以使用的语言工具越来越丰富,在本书中的实验示例还只是应用面向过程的语言进行设计和编写程序,同样的实验题,读者也可以用面向对象的语言来实现。
希望书中的实验报告示例能起到一个抛砖引玉的作用,以迎来读者更多更优良的设计范例。
二、实验步骤
随着计算机性能的提高,它所面临的软件开发的复杂度也日趋增加。
然而,编制一个10,000行的程序的难度绝不仅仅是一个5,000行的程序两倍,因此软件开发需要系统的方法。
一种常用的软件开发方法,是将软件开发过程划分为分析、设计、实现和维护四个阶段。
虽然数据结构课程中的实验题的复杂度远不如(从实际问题中提出来的)一个“真正的”软件,但为了培养一个软件工作者所应具备的科学工作的方法和作风,我们制订了如下所述完成实验的五个步骤:
(一)问题分析和任务定义
通常实验题目的陈述比较简洁,或者说是有模棱两可的含义。
因此,在进行设计之前,首先应该充分地分析和理解问题,明确问题要求做什么,限制条件是什么。
注意本步骤强调的是做什么,而不是怎么做。
对问题的描述应避开算法和所涉及的数据类型,而是对所需完成的任务作出明确的回答。
例如:
输入数据的类型、值的范围以及输入的形式;输出数据的类型、值的范围及输出的形式。
若是会话式的输入,则结束标志是什么?
是否接受非法的输入?
对非法输入的回答方式是什么等。
这一步还应该为调试程序准备好侧试数据,包括合法的输入数据和非法形式的输入数据。
(二)数据类型和系统设计
在设计这一步骤中需分逻辑设计和详细设计两步实现。
逻辑设计指的是,对问题描述中涉及的操作对象定义相应的数据类型,并按照以数据结构为中心的原则划分模块,定义主程序模块和各抽象数据类型;详细设计则为定义相应的存储结构并写出各函数的伪码算法。
在这个过程中,要综合考虑系统功能,使得系统结构清晰、合理、简单和易于调试,抽象数据类型的实现尽可能做到数据封装,基本操作的规格说明尽可能明确具体。
作为逻辑设计的结果,应写出每个抽象数据类型的定义(包括数据结构的描述和每个基本操作的规格说明),各个主要模块的算法,并画出模块之间的调用关系图。
详细设计的结果是对数据结构和基本操作的规格说明作出进一步的求精,写出数据存储结构的类型定义,按照算法书写规范用类C语言写出函数形式的算法框架。
在求精的过程中,应尽量避免陷入语言细节,不必过早表述辅助数据结构和局部变量。
(三)编码实现和静态检查
编码是把详细设计的结果进一步求精为程序设计语言程序。
程序的每行尽量不要超过60个字符。
每个函数体,即不计首部和规格说明部分一般不要超过40行,最长不得超过60行,否则应该分割成较小的函数。
要控制if语句连续嵌套的深度。
如何编写程序才能较快地完成调试是特别要注意的问题。
对于编程很熟练的读者,如果基于详细设计的伪码算法就能直接在健盘上输入程序的话,则可以不必用笔在纸上写出编码,而将这一步的工作放在上机准备之后进行,即在上机调试之前直接用键盘输入。
然而,不管你是否写出编码的程序,在上机之前,认真的静态检查是必不可少的。
多数初学者在编好程序后处于以下两种状态之一:
一种是对自己的“精心作品”的正确性确信不疑;另一种是认为上机前的任务已经完成,纠查错误是上机的工作。
这两种态度是极为有害的。
事实上,非训练有素的程序设计者编写的程序长度超过50行时,极少不含有除语法错误以外的错误。
上机动态调试决不能代替静态检查,否则调试效率将是极低的。
静态检查主要有两种方法,一是用一组测试数据手工执行程序(通常应先分模块检查);二是通过阅读或给别人讲解自已的程序而深入全面地理解程序逻辑,在这个过程中再加入一些注释。
如果程序中逻辑概念清楚,后者将比前者有效。
(四)上机准备和上机调试
上机准备包括以下几个方面
(1)高级语言文本(体现于编译程序用户手册)的扩充和限制。
例如,常用的TurboC(C++)和MicrosoftC(C++)与标准C(C++)的差别,以及相互之间的差别。
(2)如果使用C或C++语言,要特别注意与教科书的类C语言之间的细徽差别。
(3)熟悉机器的操作系统和语言集成环境的用户手册,尤其是最常用的命令操作,以便顺利进行上机的基本活动.
(4)掌握调试工具,考虑调试方案,设计测试数据并手工得出正确结果。
“磨刀不误砍柴工”。
计算机各专业的学生应该能够熟练运用高级语言的调试功能调试程序。
调试最好分模块进行,自底向上,即先调试低层函数。
必要时可以另写一个调用驱动程序。
这种表面上麻烦的工作买际上可以大大降低调试所面临的复杂性,提高调试工作效率。
在调试过程中可以不断借助DEBUG的各种功能,提高调试效率.调试中遇到的各种异常现象往往是预料不到的,此时不应“冥思苦想”而应动手确定疑点,通过修改程序来证实它或绕过它。
调试正确后,认真整理源程序及其注释,打印出带有完整注释的且格式良好的源程序清单和结果
(五)总结和整理实验报告
三、实验报告规范
实验报告的开头应给出题目、班级、姓名、学号和完成日期,并包括以下七个内容:
1.需求分析
以无歧义的陈述说明程序设计的任务,强调的是程序要做什么。
明确规定:
(1)输入的形式和输入值的范围;
(2)输出的形式;
(3)程序所能达到的功能;
(4)测试数据:
包括正确的输入及其输出结果和含有错误的输入及其输出结果。
2.概要设计
说明本程序中用到的所有抽象数据类型的定义、主程序的流程以及各程序模块之间的层次(调用)关系。
3.详细设计
实现概要设计中定义的所有数据类型,对每个操作只需要写出伪码算法;对主程序和其他模块也都需要写出伪码算法(伪码算法达到的详细程度建议为:
按照伪码算法可以在计算机键盘直接输入高级程序设计语言程序);画出函数的调用关系图。
4.调试分析
内容包括:
(1)调试过程中遇到的问题是如何解决的以及对设计与实现的回顾讨论和分析;
(2)算法的时空分析(包括基本操作和其他算法的时间复杂度和空间复杂度的分析)和改进设想;
(3)经验和体会等。
5.用户使用说明
说明如何使用你编写的程序,详细列出每一步的操作步骤
6.测试结果
列出你的测试结果,包括输入和输出。
这里的测试数据应该完整和严格,最好多于需求分析中所列。
7.附录
带注释的源程序。
如果源程序另外提交,这里可以只列出程序文件名的清单。
在以下各实验单元中都提供了实验报告实例。
值得注意的是,实验报告的各种文档资料,如上述中的前三部分要在程序开发的过程中逐渐充实形成,而不是最后补写。
四、算法书写规范
(1)算法说明
算法说明,也称为(算法)规格说明,是一个完整算法不可缺少的部分,应该在算法头(即过程或函数首部)之下以注释的形式写明如下内容:
指明算法的功能;参数表中各参量的含意和输入输出属性;算法中引用了哪些全局变量或外部定义的变量,它们的作用、入口初值以及应满足哪些限制条件,例如,链表是否带头结点、表中元素是否有序、按递增还是递减方式有序等。
必要时,算法说明还可用来陈述算法思想、采用的存储结构等。
递归算法的说明特别重要,读者应该力求将它写成算法的严格定义。
算法说明应该在开始设计算法时就写明,可以在算法设计过程中作一些补充和修改,但是切忌最后补写。
对于递归算法的情况,这一点尤其重要。
这样做也是递归算法设计的正确而有效的途径,在算法设计(即解决一个问题)的过程中,能否利用自身的处理能力来解决所划分出的一个或几个子问题,全凭检查自身的规格说明而定。
书写(递归)算法的规格说明时,应该忽略它如何实现或者假定它能够实现。
如何实现的问题正是接下去要做的事。
算法说明书写得不好或不完全时,往往失去了评判一个算法正确与否的标准。
书写恰当而又简洁的算法说明是一项具有很强技巧性的活动,通常要经过不断的练习才能达到。
在本节的末尾将列出一些规格说明的例子。
(2)注释与断言
在难懂的语句和关健的语句(段)之后加以注释可以大大提高程序的可读性。
注释要恰当,并非越多越好,此外,注释句的抽象程度应略高于语句(段),例如,应避免用“i增加1”来注释语句“i++;"。
断言是注释的一种特殊写法,是一类特别重要的注释。
它是一个逻辑谓词,陈述算法执行到这点时应满足的条件。
多写断言式的注释,甚至以断言引导算法段的设计,是提高算法的结构良好性、避免错误和增强可读性的有效手段,是特别值得提倡的。
其中最重要的是算法的入口断言和else分支断言。
注意,正确的算法也只能在输入参数值合法的前提下得出正确的结果。
如果算法不含参数合法性检测代码段,书写入口断言是最低限度的要求。
(3)输入和翰出
算法的输入和抽出可以通过三种途径实现。
第一种是通过scanf和printf语句实现,其特点是实现了算法与计算环境外部的信息交换;第二种是以算法头中参数表里显式列出的参量作为输入/输出的媒介。
第三种是通过全局变量或外部变量隐式地传递信息。
后两种方法的特点是实现了一个算法与其调用者之间的信息交换。
如果一个算法是定义在某个数据结构之上的几个操作之一,该数据结构可以不列在算法的参数表中。
在其他情况下,应尽量避免使用第三种方法。
(4)错误处理
尽可能使用函数值返回算法的执行状态(正确/错误,或是错误代码等),便于调用者处理异常情况,有利于培养良好的程序设计习惯。
(5)语句选用和算法结构
赋值语句、if分支语句和while(或for)循环语句是最基本的三种语句,仅用此三种语句就足以对付一切算法的设计了。
实际上,不仅是“足够”而且是“最好”。
这样做对于提高结构良好性和可读性、避免逻辑错误是有益和有效的。
switch分支语句是广义的if分支语句,在分支条件复杂时选用可以避免if语句的多重嵌套,有助于提高算法的可读性,也是一个鼓励使用的语句。
一般情况下,不准使用goto语句,个别的特殊情况除外。
(6)基本运算
如果题目中未明确要求用某种数据结构上的基本运算编写算法,不得直接利用教科书中给出的基本运算。
如果非用不可,则要求将所用到的所有基本运算同时实现。
(7)几点建议
建议以图说明算法;
建议在算法书写完毕后,用边界条件的输入参数值验证一下算法能否正确执行。
例如,对于顺序表插入算法,空表是一个边界条件。
(8)附例:
算法规格说明例释
例1
StatusDeleteK(SqLista,inti,intk)
//本过程从顺序存储结构的线性表a中删除第i个元素起的k个元索.
例2
floatEvaluate(SqPolypn,floatx)
//多项式pn.data[i].coef存放ai,pn.data[i].exp存放ei,(i=1,2,…,m)。
//此外,入口时要求O 例3入栈push算法 voidpush(TwoWayStack&tws,inti,EIemTypex,bool&overflow) //两栈(标号0,1)共享空间tws.elem[0..m-1],栈底在两端。 tws.top[0] //和tws.top[1〕为两栈顶指针。 tws.data[O..tws.top[0]〕为栈0, //tws.data[tws.top[1]..m-1]为栈1。 本算法将x推入栈i(=0,1)。 //若入口时栈满,则不改变栈且返回overflow为TRUE. 例4出列del_queue算法 booldequeue(EIemType&x) //循环队列squeue[(m+rear-quelen+1)MODm]之上的元素出列操作。 //quelen为队列长度。 当quelen=0时,返回FALSE,否则返回TRUE, //且用x返回出列的元素值。 五、参考书目 1.《数据结构》(C语言版)严蔚敏等清华大学出版社 2.《数据结构题集》(C语言版)严蔚敏等清华大学出版社 3.《DATASTRUCTUREWITHC++》WilliamFord,WilliamTopp清华大学出版社(影印版) 实验〇顺序表与链表 实验时数: 2学时 一、实验目的: (1)掌握顺序表的存储结构形式及其描述和基本运算的实现。 (2)熟练掌握动态链表结构及有关算法的设计。 (3)掌握用链表表示特定形式的数据的方法,并能编写出有关运算的算法。 (4)理解单循环链表及双循环链表的特点。 二、实验内容: 1、顺序表的应用 (1).输入一组整型元素序列,建立顺序表。 (2).实现该顺序表的遍历。 (3).在该顺序表中顺序查找某一元素,查找成功返回1,否则返回0。 (4).判断该顺序表中元素是否对称,对称返回1,否则返回0。 (5).实现把该表中所有奇数排在偶数之前,即表的前面为奇数,后面为偶数。 (6).输入整型元素序列利用有序表插入算法建立一个有序表。 (7).利用算法6建立两个非递减有序表并把它们合并成一个非递减有序表。 (8).编写一个主函数,调试上述算法。 2、链表的应用 (1).键盘输入一组元素,建立一个无头结点的单向链表(无序)。 (2).遍历(打印)单向链表。 (3).把单向链表中元素逆置(不允许申请新的结点空间)。 (4).在单向链表中删除所有的偶数元素结点。 (5).对链表排序,排序后链表元素按照非递减方式排列(注意: 排序时如果要交换两个结点的顺序,不得通过交换结点的内容,而需要使用改变指针的方式交换结点的位置。 建议使用直接插入排序算法)。 (6).利用算法5建立两个非递减有序单向链表,然后合并成一个非递减链表。 (7).利用算法1建立的链表,删除链表中的重复元素。 (8).利用算法1建立的链表,实现将其分解成两个链表,其中一个全部为奇数,另一个全部为偶数(尽量利用已知的存储空间)。 (9).判断算法1和算法5生成单链表所表示的集合是否相等。 (10).在主函数中设计一个简单的菜单,分别调试上述算法。 【选作内容】 (11).利用算法5建立两个非递减有序单向链表,然后合并成一个非递增链表。 (12).采用单向链表实现一元多项式的存储并实现两个多项式相加并输出结果。 【注意事项】 1.为了看到操作结果,在每次链表操作后,均需打印出链表的内容。 2.单向链表的操作是数据结构的基础,一定要注意对这部分的常见算法的理解。 3.演示及撰写实验报告时,一定要注意各种情况的测试。 比如算法7删除节点的操作,至少要测试以下几种情况: (1)找不到元素; (2)要删除的元素是链表第一个节点;(3)要删除的元素是链表的最后一个节点;(4)要删除的元素在链表的中间。 实验〇实验报告示例 接下来的几页是一个简单的实验报告示例。 这里为了达到完整性,在下一页显示了一个封面,也即: 每份实验报告都需加一个封面。 封面后最好能再增加一个目录。 由于实验0和以后的实验在实验报告的撰写上有一定区别,毕竟实验0的每一个小程序都比较简单,所以请读者仔细阅读实验0到实验2的实验报告示例,从而学会撰写一份好的实验报告。 数据结构实验报告 实验〇: 顺序表与链表 专业: 班级: 姓名: 学号: 指导教师: 日期: 一、实验目的 熟练掌握动态链表结构及有关算法的设计。 掌握用链表表示特定形式的数据的方法,并能编写出有关运算的算法。 二、实验内容 链式存储结构及应用 1.键盘输入一组元素,尾部插入法建立一个单向链表。 2.在单向链表中删除一个结点。 3.在链表中插入一个元素。 三、需求分析 1.输入的形式和输入值的范围 建立链表时输入的都是整数,输入0代表输入的结束,插入元素时需要输入插入的位置和元素的值,删除元素时输入删除元素的值。 2.输出形式 所有操作在出现错误时都会有提示,并且在任意一个操作结束后都会输出操作后的链表。 3.程序所能达到的功能 完成链表的生成,任意位置插入元素、删除,实现链表数据的排序,删除链表中相同的元素,并且比较两个链表是否相等以及将两链表合成一个有序的链表。 4.测试数据: 见第八部分。 四、概要设计 (1)本程序包含的函数 主函数main() 显示操作菜单函数mainmenu() 显示单链表内容函数print() 尾部插入建立链表函数creatb() 插入元素函数insert() 删除函数Delete() 其中main函数负责操作其他的函数。 (2)各函数的功能设计为: structstudent*creatb(): 建立一个链表A(尾部插入建立的链表),并且返回这个链表的头指针. structstudent*insert(inta,intb,structstudent*head): 接受2个int类型的数据a和b(其中a为插入的位置,b为插入的数据),head为需要操作的链表的头指针. structstudent*Delete(inta,structstudent*head): 删除链表A中和数据a相同的结点.返回操作后的链表的头指针. structstudent*Delete2(structstudent*head): 删除链表A中相同的元素返回操作后的链表的头指针. structstudent*creath(): 建立链表B(头部插入建立的链表),返回头指针. structstudent*paixu(structstudent*head): 调用2次该函数将A和B中的数据实现非递减的顺序排列,返回操作后链表的头指针. structstudent*merge(structstudent*head1,structstudent*head2): 将链表A和B合成一个非递减的链表,返回新链表的头指针. voidcompare(structstudent*head1,structstudent*head2,intflag): 比较两链表是否相等.其中flag为判断用户是否进行了两链表的排序操作,当flag为1时,表示进行了排序操作.为0时表示没用进行排序操作(这样可以节省运算的时间). mainMenu(): 打印出菜单 五、详细设计 (1)结点类型和指针类型: structstudent { intnum; structstudent*next; }; (2)单链表的基本操作 函数的算法: (1)voidmainmenu() 显示主菜单的函数 (2)voidprint(structstudent*head) 输出函数,接受一个头指针,函数判断这个指针是不是为空,是的话就有提示语句,若不是则用while循环将链表的数据顺序输出. (3)structstudent*creatb() 尾部插入法创建链表的函数.首先创建3个student结构的指针p1,p2,head.第一次输入数据时p1接收输入的数据.然后让p2=p1,head=p1.再用while循环实现连续的输入.p1继续接受数据,调用语句p2->next=p1,将结点连接起来.然后p1和p2同时后移.当输入0时断开循环.返回head. (4)structstudent*insert(inta,intb,structstudent*head)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 实验 指导书