上海交大 数据结构与算法实验指导书.docx
- 文档编号:3995898
- 上传时间:2022-11-27
- 格式:DOCX
- 页数:13
- 大小:80.90KB
上海交大 数据结构与算法实验指导书.docx
《上海交大 数据结构与算法实验指导书.docx》由会员分享,可在线阅读,更多相关《上海交大 数据结构与算法实验指导书.docx(13页珍藏版)》请在冰豆网上搜索。
上海交大数据结构与算法实验指导书
数据结构与算法实习指导书
上海交通大学电院数据结构大平台课程组
目录
1.关于实习步骤的要求和建议
2.上机实习
实习一线性结构
实习二树和二叉树
实习三查找
实习四图
3.实习报告样例
1.关于实习步骤的要求和建议
从以往的教学事先实习的经验来看,在初学阶段执行严格的实习步骤规范(包括上机操作规范),机时利用率会大大提高,有助于养成良好的程序编制风格,培养严谨、科学、高效的工作方式。
在以往的教学实践中,经常发现很多学生抱怨说,化了两个小时才找出一个错误,甚至一无所获。
他们不明白造成这种情况的原因,正是他们自己。
有的学生不屑于按实习步骤规范去做,甚至对于实习步骤的要求和建议看都不看一遍,认为那是浪费时间,这是及其害的。
实习步骤规范不但可以培养科学化的工作作风,而且还能有效地避免错误。
具体的步骤机规范如下:
1.问题分析与系统的结构设计:
充分地分析和理解问题本身,弄清要求作什么,限制条件是什么。
按照以数据结构为中心的原则划分模块,即定义数据结构及其在这些结构之上的操作,使得对数据结构的存取通过这些操作加以实现。
在这个过程中,要综合考虑系统功能。
要考虑系统结构清晰、合理、简单并且易于调试。
最后写出每个子程序(过程或函数)的规格说明,列出它们之间的调用关系,可以使用调用关系图表示则更加清晰,这样便完成了系统结构设计。
2.详细设计和编码
详细设计的目的是对子程序(过程或函数)的进一步求精。
用IF、WHILE和赋值语句等,以及自然语言写出算法的框架。
利用自然语言的目的是避免陷入细节。
在编码时,可以对详细设计的结果进一步求精,用高级语言表示出来。
程序的每一行最好不超过60个字符。
每个子程序(或过程、函数)通常不要太长,以40行为宜。
子程序(或过程、函数)包含的程序行数太多,易于造成理解的困难。
控制IF、WHILE等语句的连续嵌套的深度应加以控制。
程序的目的性必须明确。
对每一段程序完成的作用,除非常明显的除外(如:
x=x+1;注释为x加1,没有什么意义),都应加以注释。
这会对程序的调试提供很多方便。
另外,根据情况可以设立若干调试点,即输出若干信息,用于验证和你的设想是否一致。
另外,对于输入输出语句,必须对它们的作用加以说明。
否则,在调试程序时,无法了解系统需要输入什么,系统输出的又是什么。
程序的书写,必须按照一定的规范,如保留字小写时涂黑等等。
3.上机准备和静态检查
上机准备:
●高级语言文本
●熟悉机器的用户手册,熟悉常用的命令。
●准备调试的工具,考虑调试方案。
如果机器上没有现成的调试工具可供利用,可以自己先设计一些以供使用。
●静态检查
自己用一组数据手动执行程序;或同同学一起阅读自己的程序,以全面地了解该程序的逻辑。
4.上机调试程序
自底向上,先调试底层模块,再调试上层模块。
最后,整个程序进行联调。
调试正确后将源程序和运行结果加以列印输出。
5.实习报告的整理
●需求及规格说明
问题描述,求解的问题是什么。
●设计:
设计思想:
存储结构、主要的算法思想。
设计表示:
子程序(过程或函数)的规格说明,通过调用关系图表示它们之间的调用关系。
实现注释:
详细设计表示:
主要算法的框架。
●用户手册:
使用说明。
●调试报告:
问题是如何解决的,讨论与分析、改进设想、经验与体会、时空复杂度等。
●附录
源程序清单和结果:
源程序必须有注释,以及必要的测试数据和运行结果数据。
提倡用英文描述。
●实验报告要求:
在程序开发过程中,逐步形成各种必要的文档及资料。
可以写在实验报告纸上,或以电子文档的形式进行书写。
2.上机实习
●以下的实习题目配合课程的进度,请同学们务必自己完成。
为了锻炼自己的应用各种不同的数据结构的能力,尽可能的多作一些题目是非常必要的。
在完成各种不同题目的过程中,对各种算法的时、空复杂性的分析,将帮助您在更高的角度解决各种应用问题。
●为了减轻同学的负担,我们对同学的实习报告进行了精简。
本实习报告中的题目分以下几种类型:
1、实习题:
必须按照实习报告的规范完成。
每个实习的第一题都是实习题,必须编写详细的实习报告。
实习报告的书写方法,请参阅本指导书的第3部分:
实习报告的样例。
2、作业题:
同样必须完成。
只需交代清楚题目的解法,辅以求解程序和注释,使得别人能够看懂你的算法和程序即可。
不必象实习题那样书写完整的实习报告。
3、选作题:
鼓励同学选作的题目,尤其是学有余力的同学。
以下为各次实习作业:
实习一线性结构
1、(实习题)请写出计算两个以单链接表表示的多项式相乘的程序。
2、(作业题)已知两个单链表A和B分别表示两个集合,其元素递增排列。
请编写程序求集合A和B的交集C=AB,要求单链表C按其元素递增排列,并利用原表(即表A和表B)的结点空间存放表C。
3、(作业题)假设有二个栈共同使用一块顺序存储的空间,为简单起见可设为共同使用数组inta〔200〕。
它们的栈底分别设在数组的两端,而栈顶指针在进行插入操作时,向中间方向移动。
请给出进出栈的程序。
4、(选作题)将具有头结点的单链表的所有指针全部进行倒向。
要求使用的额外空间只能为O
(1),时间代价只能为O(n),其中n为结点个数。
注意:
如下图1所示的单链表,倒向之后将如图2所示。
图1、倒向之前的单链表
图2、倒向之后的单链表
实习二树和二叉树
1、(实习题)请编写一个程序,确定二叉树的特征。
如:
每个节点的层次,从根到该节点的枝长(路径长度),子孙的个数及祖先的个数。
每个节点在前序、中序、后序中的访问的序号。
2、(作业题)设二叉树的结点的数据场之值仅为一大写英文字母。
其前序和中序的遍历结果(打印结点的数据场之值)分别保存在字符串数组preorder[N]及inorder[N]之中,其中N未常数。
请设计程序以标准形式形式存储保存该二叉树。
3、(作业题)设树的根结点的层号为1,而其他各层上的结点的层号比其父结点的层号大1。
另外设该树中的结点的数据场之值为正整数。
这样数对(Ik,Wk)就表示了该树中的结点的层号和其数据场之值。
从键盘上依次输入m个数对,如:
(I1,W1),(I2,W2),……,(Im,Wm);这些数对是按照结点的前序序列给出的。
注意这是用层号+前序表示一棵树的方法。
如:
(1,A),(2,B),(2,C),(3,E),(4,G),(3,F),(2,D),(3,X),(3,Y),(3,Z);它所表示的树如图3所示。
请编写程序,以标准形式存储这棵树。
为了简单起见,可设这棵树上的结点的度数最大为3,结点的存储形式为:
dataparentson1son2son3
其中:
data域为结点的数据场,parent域为结点的父亲结点的地址,son1,son2,son3分别给出结点的三个儿子的地址。
图3、一课三次树
4、(选作题)用标准形式给出了一棵度为三的树(每个结点包含数据场、指向儿子节点的指针场,可参阅上题)。
设该三次树的数据场的值为一个字符,请编写一个程序,以树的两维形式表示打印节点的值。
注意:
图3即为树的二维表示形式。
在实现时,为了方便可用打点的方法代替直线。
实习三查找
1、(实习题)从键盘上输入一串正整数,最后输入-1作为输入结束的标志。
如输入的序列为:
2,5,7,23,48,96,……,-1。
请以这些正整数的值作为二叉排序树中的结点的数据场之值,建立一棵二叉排序树。
注意:
请采用动态存储方法保存这棵二叉排序树,事先并未知道该二叉排序树中的结点的个数。
2、(作业题)已知一棵排序二叉树,树中结点的形式为:
datainfoleftright
其中,data给出结点的数据场,info给出本结点的左子树中的结点总数,left和right分别给出本结点的左儿子和右儿子的地址。
数据场data和info的类型皆为int。
又已知该二叉排序树的根结点的地址为root。
请设计二个函数,分别实现下述功能:
1.按递增序找出该二叉排序树中的第i个小的结点。
2.插入数据场之值为x的结点,并仍应保持该二叉排序树的性质不
变。
3、(作业题)已知一棵二叉排序树,其根结点的地址为root。
请编写一个程序,构造出一棵具有相同结点的完全二叉树,且它同样是二叉排序树。
4、(选作题)在平衡的排序二叉树的中,试编写删除具有给定关键字的结点的函数。
实习四图
1、(实习题)以数偶的形式依次从键盘上输入一串数据。
如:
(A,B)为从起始结点(其数据场之值为一大写的英文字母A),到终止结点(其数据场之值为一大写的英文字母B)的无向边。
最后输入(Z,Z)表示输入结束。
请用无向图的邻接多重表存储该无向图,并注意一定要使用动态存储结构。
2、(作业题)已知一以动态存储结构形式存储的,以邻接多重表表示的无向图。
请用KRUSKAL算法求出它的最小代价生成树。
3、(作业题)已知一以邻接矩阵形式存储的AOV图。
请求出它的所有的合理的拓扑排序的序列。
4、(选作题)已知一以动态存储结构形式存储的,以邻接表表示的有向图。
请求出它的强连通分量。
3、实习报告样例
一、实习题:
约瑟夫(Josephus)问题:
设有n个人围成一个圆圈,任意给定一个正整数m,从第一个人开始顺时针计数,计到第m个人,将其从圆圈中除去。
然后再从下一个人开始,周而复始,直到圆圈中只剩一个人为止,那么剩下的那个人就是赢家。
1.需求分析和说明
分析约瑟夫问题:
n个人围成圈,从第一个人开始,数到第m个人,删除并以下一个
人开始进行第二轮操作,直到最后一个人作为优胜者。
例如n=6,m=3,处理过程下图。
2.设计
n个人围圈,形成线性关系;处理为逐个删除,故用链式结构合适;又人员围成圆圈,所以此链式结构采用循环方式较好;排号按照一个方向进行,故数据结构采用带头结点的单向循环链表。
假设人员以首次的编号命名,对每个人员采用编号和姓名加以描述。
存储结构:
structperson{//定义人员信息,包括序号和姓名
intno;
charname[10];
};
//circlinklist.h
单向循环链表类
template
CircLinkListNode
CircLinkListNode
CircLinkListNode
intposition;//表中当前元素所在的元素序号(位置)
public:
CircLinkList();//构造函数
~CircLinkList();//析构函数
voidClear();//链表置空
intLength()const{returnsize;};//求链表长度
boolIsEmpty()const{return(size==0);};//判断链表是否空
boolIsEnd()const{return(currPtr=tail);};//当前结点是否是尾结点
intCurrentPosition()const{returnposition;};//返回当前结点的序号
ElemTypeData()const;//返回当前指针所指的结点中的元素值
voidGoNext();//将当前指针指向当前结点后面的一个结点
voidReset(intpos);//将当前指针指向序号为pos的结点
CircLinkListNode
//查找从当前结点起第一个元素值为e的结点
//各种位置上的插入操作
voidInsertFront(constElemTypee);
//在首结点位置上插入元素值为e的新结点
voidInsertTail(constElemTypee);
//在尾结点之后插入元素值为e的新结点,使其成为新的尾结点
voidInsertAt(constElemTypee);
//在当前结点位置上插入元素值为e的新结点,
//原来的当前结点成为其后一个结点
voidInsertAfter(constElemTypee);
//在当前结点之后插入元素值为e的新结点
ElemTypeRemoveFront();//删除首结点,并返回其元素值
ElemTypeRemoveAt();//删除当前结点,并返回其元素值
};
算法思想:
声明一个person类型的单向循环链表。
从键盘顺序输入n个人的姓名,建立约瑟夫环。
计数并逐个读取并删除第m个人,直到链表为空,其中最后一个被读出的即优胜者。
调用关系:
程序任务简单,故设计在一个main()函数内,只设计类成员函数的调用,无另外的子程序或函数。
算法实现框架:
3.用户手册:
运行程序,按照屏幕提示分别输入圈内人数n,正整数m和n个人的姓名,之后屏幕将显示按照输入次序排好的人员被逐个删除的次序,最后显示最后的出优胜者。
4.调试报告:
时间复杂度分析:
该算法在建立时的时间复杂度为O(n),删除时时间耗费在逐个数元素上,按照第m个删除的原则,不妨将n个元素分成若干组,每组m个人,n个人最多分n/m+1组。
扩展
最后一组,使其也是m个人,对组内元素从1到m排号,每组排号为m的只数到一次便被删除;第二圈数每组排号为1的被删除,每个元素数过2次;第三圈数每组排号为2的被删除,每个元素数过3次;最后,第m圈,每组排号为m-1的被删除,每个元素数过m次;故总删除总的时间为:
(n/m+1)(1+2+3+…+m)=m(m+1)(n/m+1)/2,时间
复杂度为:
O(n*m);缩小最后一组,使其是0个人,同上可得删除总的时间为:
(n/m)(1+2+3+…+m)=m(m+1)(n/m)/2,时间复杂度也为:
O(n*m);
综合建立和删除,算法时间复杂度为:
O(n*m)
算法改进思路:
在对元素数数删除过程中,总是要去判断是否是头结点并绕过它,可以改进一下,去掉头结点,由此看来,并非链式结构带有头结点都有益处。
改进后性能可以提高,但时间复杂度依然为:
O(n*m)
5.附录
源程序清单
//josephusRing.cpp
#include
#include"circlinklist.h"
structperson{//定义人员信息,包括序号和姓名
intno;
charname[10];
};
voidmain(){
CircLinkList
persontemp;
inti;
intn,m;//共有n个人,计数到m删除
//从键盘输入总人数n
cout<<"Inputthenumberofpeople:
";
cin>>n;
cout< //从键盘输入计数标准m cout<<"Inputcountnumber: "; cin>>m; cout< //顺序输入n个人的姓名,建立约瑟夫环 cout<<"Inputeveryperson'sname: "< for(i=1;i<=n;i++) {cin>>temp.name;//输入姓名 temp.no=i;//将输入次序作为人员编号 josephusRing.InsertTail(temp);//将新来人员信息加入到链表尾部 } //计数并逐个删除第m个人,直到链表为空 josephusRing.Reset (1);//当前结点设置为首结点 i=0; cout<<"Deletedorder: "< while(! josephusRing.IsEmpty()){//以链表为空作为结束条件 josephusRing.GoNext();//将当前结点设置为当前结点的下一个结点 if(josephusRing.CurrentPosition()! =0)i++; //如果当前结点不是头结点,计数值加1,否则跳过头结点,计数值不变 if(i==m){//如果计数到m,要删除当前结点 temp=josephusRing.RemoveAt(); //删除当前结点,当前结点顺其next指针变为下一个结点 cout< //如果当前结点非头结点,已经进入下一轮计数,计数器值设置为1 //如果当前结点为头结点,还未进入下一轮计数,计数器值设置为0 if(josephusRing.CurrentPosition()! =0) i=1; elsei=0; } } cout<<"Thewinneris: "< } 测试数据: 6个人,非别为Ann、Bill、Cherry、David、Elan、Fred,每次删除第3个人,预计运行结果为: 逐次删除Cherry、Fred、David、Bill、Elan、Ann,赢者为: Ann 测试及运行结果: Inputthenumberofpeople: 6 Inputeveryperson'sname: 3 Inputeveryperson'sname: Ann Bill Cherry David Elan Fred Deletedorder: 3Cherry 6Fred 4David 2Bill 5Elan 1Ann Thewinneris: Ann 运行结果分析: 运行结果符合预定目标
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 上海交大 数据结构与算法实验指导书 数据结构 算法 实验 指导书
![提示](https://static.bdocx.com/images/bang_tan.gif)