数据结构实验指导书.docx
《数据结构实验指导书.docx》由会员分享,可在线阅读,更多相关《数据结构实验指导书.docx(30页珍藏版)》请在冰豆网上搜索。
数据结构实验指导书
给大家的实验文档中分为一下几部分
1.实验准备:
请好好复习C语言,有时间也复习一下C++。
2.c语言实验教案中是一些c语言的基础知识,包括VC环境的使用和程序的调试,希望对c语言已经忘记的同学好好看看复习一下。
(程序的编写调试是长年累月的过程,需要不断的积累,写得多了,程序调试的多了,自然就熟练了)
3.对应的flash课件:
其中是一些实验的flash课件演示,给大家做一下参考
4.实验指导书和实验教案大家在做每个实验前都需要看看。
阅读的时候,可以使用【视图】|【文档结构图】,可以比较自由跳到相应位置
5.总体实验难度比较大,时间紧,单靠实验课上的几个学时,作为初学者是无法完成的,需要大家在课前课后尽自己最大的努力。
6.每个实验的代码编写可以用c也可以用c++
7.实验前要写预习报告,格式跟实验报告一致,实验结束后,可以直接在其上修改实验代码,加上实验小结,作为实验报告上交。
实验报告必须手写!
!
代码部分可以只写关键代码。
实验安排
1、多项式加减法,2学时
2、栈和队列的应用,2学时
3、迷宫,4学时
4、二叉树的建立和遍历,4学时
5、归并排序,2学时
6、图,2学时
实验一多项式加减法
一、实验目的
通过实现多项式的加减法,对链表有更深入的了解
二、实验内容
问题描述:
设计一个一元稀疏多项式简单的加减法计算器
实现要求:
一元稀疏多项式简单计算器的基本功能是:
(1)输入并建立多项式:
;
(2)输出多项式
(3)多项式A和B相加,建立多项式C=A+B,并输出相加的结果多项式C
(4)选作:
多项式A和B相减,建立多项式C=A-B,并输出相加的结果多项式D
方法说明:
(1)多项式的输入与存储
用带表头结点的单链表存储多项式,链表中的每个节点分别存储多项式各项的系数和指数,即每从键盘输入多项式的一对数(系数,指数),可对应建立链表的一个结点。
每个节点的结构为:
建立两个链表,其中pa和pb分别为它们的头指针:
Pa
pb
结果链表
Pa(或者是Pc)
Pc
(2)多项式数据类型的定义
structtagNode
{
floatcoef;
intexp;
structtagNode*next;
};
typedefstructtagNodeNode;
typedefstructtagNode*pNode;
(3)主要算法
①创建两个链表,分别存放多项式1和多项式2,这两个链表中的节点是按指数降序或者升序排列的
②多项式相加或相减,下面给出多项式相加的部分实现
/*
下面的函数实现两个多项式的相加,要相加的链表分别由pa和pb指向(其中,pa,pb都是分配了空间的头结点)。
相加的结果直接由pa指向的链表保存,即是在pa链表中添加或删除(当系数因为相加为0的情况下)一些结点,构成结果。
这里要相加的链表中指数都是按从小到大的顺序排列好了的,是升序链表。
*/
voidadd_poly(Node*pa,Node*pb)
{
Node*p=pa->pNext;//链表1,将来的结果也放在此
Node*q=pb->pNext;//链表2
Node*pre=pa;
Node*u;//临时用
floatx;
while(p!
=NULL&&q!
=NULL)//当两个链表都不为空
{
if(p->expexp)//比较链表1跟链表2当前节点的指数大小,链表1也是存放结果的地方
{
pre=p;
p=p->pNext;//p指向要比较的下一个结点。
pre指向的是结果链表的最后一个结点。
}
elseif(p->exp==q->exp)//假如链表1和链表2的指数相等,就要系数相加
{
x=p->coef+q->coef;
if(x!
=0)//相加后的系数不为0,有必要保留一个结点就可以了
{
p->coef=x;
pre=p;
}
else//如果相加后,系数不是0,不需要保留任何一个结点,在这里删除链表1的结点,下面删除链表2的结点
{
pre->pNext=p->pNext;//保持链表1的连续性
free(p);
}
p=pre->pNext;//p指向要比较的下一个结点
//下面的代码是进行链表2结点的删除工作,因为指数相等,仅仅需要保留一个结点就可以了
//而结果直接保存在链表1中,所以,删除链表2的结点。
u=q;
q=q->pNext;
free(u);
}
else//如果链表2的当前节点指数小,那么要把链表2的当前节点加入到结果链表中(即是链表1)
{//相当于把结点插入到链表1中,用u作为临时变量,保存链表2的下一个当前节点的位置。
u=q->pNext;
q->pNext=p;
pre->pNext=q;
pre=q;
q=u;
}
}
if(q)//如果链表2比链表1长,那么需要把链表2多余的部分加入结果链表中。
链表1比链表2长,则什么都不用做。
{
pre->pNext=q;
}
free(pb);
}
③输出结果多项式
实验二栈和队列的应用
一、实验目的
1、掌握栈的编写和应用
2、掌握队列的编写和应用
二、实验内容
1、分别使用STL中的栈和自己编写的栈类,实现序列的反转
(1)简单修改下面代码,实现使用STL中的栈进行序列的反转,编译运行
#include
intmain()
/*Pre:
Theusersuppliesanintegernandndecimalnumbers.
Post:
Thenumbersareprintedinreverseorder.
Uses:
TheSTLclassstackanditsmethods*/
{
intn;
doubleitem;
stacknumbers;//declaresandinitializesastackofnumbers
cout<<"Typeinanintegernfollowedbyndecimalnumbers."
<<<"Thenumberswillbeprintedinreverseorder."
<cin>>n;
for(inti=0;icin>>item;
numbers.push(item);
}
cout<while(!
numbers.empty()){
cout<numbers.pop();
}
cout<}
提示:
(1)由于程序是用了STL(标准模板库,可以简单的看成是一个函数库,在其中有各种有用的类、函数和算法),栈在其中有实现。
栈在STL中的实现用到了类模板,也就是说其栈是独立于类型的,模板提供参数化类型,也就是能将类型名作为参数传递给接收方来建立类或函数。
比如stacknumbers;中就是声明了一个栈,这个栈中存放的数据类型为double。
(2)要使用c++的输入输出需要加上几行语句如下,因为cout和cin是在命名空间std中的:
#include
usingnamespacestd;
(2)自己编写程序实现栈
该栈可以用链表实现,也可以用数组实现。
C语言或者c++描述都可以。
该实现的栈要求至少具有
(1)压栈
(2)出栈(3)判断栈是否空(4)取栈顶元素值等功能。
如果用数组实现该栈,则该栈还需要具有下面函数(5)判断栈是否满
(3)使用自己编写的栈,进行序列的反转
2、自己编写程序,实现队列,并自己编写主程序测试该队列(选作)
该队列可以使用链表实现,也可以使用数组实现,C语言或者c++描述都可以。
实现队列后,再编写一个简单的主函数,测试自己编写的队列的各个功能。
要求:
自己实现的队列至少需要有如下功能:
(1)进队
(2)出队(3)取队首元素(4)判断队列是否为空。
如果是用数组实现的队列,至少还需要具有下面的函数(5)判断队列是否满
附录:
STL中栈的使用
#pragmawarning(disable:
4786)
#include
#include
usingnamespacestd;
typedefstackSTACK_INT;
intmain()
{
STACK_INTstack1;
cout<<"stack1.empty()returned"<<
(stack1.empty()?
"true":
"false")<cout<<"stack1.push
(2)"<stack1.push
(2);
if(!
stack1.empty())//Function3
cout<<"stack1.top()returned"<<
stack1.top()<cout<<"stack1.push(5)"<stack1.push(5);
if(!
stack1.empty())//Function3
cout<<"stack1.top()returned"<<
stack1.top()<cout<<"stack1.push(11)"<stack1.push(11);
if(!
stack1.empty())//Function3
cout<<"stack1.top()returned"<<
stack1.top()<//Modifythetopitem.Setitto6.
if(!
stack1.empty()){//Function3
cout<<"stack1.top()=6;"<stack1.top()=6;//Function1
}
//Repeatuntilstackisempty
while(!
stack1.empty()){//Function3
constint&t=stack1.top();//Function2
cout<<"stack1.top()returned"<cout<<"stack1.pop()"<stack1.pop();
}
}
运行结果:
stack1.empty()returnedtrue
stack1.push
(2)
stack1.top()returned2
stack1.push(5)
stack1.top()returned5
stack1.push(11)
stack1.top()returned11
stack1.top()=6;
stack1.top()returned6
stack1.pop()
stack1.top()returned5
stack1.pop()
stack1.top()returned2
stack1.pop()
实验三迷宫
一、实验目的
1、了解回溯法在求解迷宫问题中的应用
2、进一步掌握栈的使用
二、实验内容
用回溯法求解迷宫问题,可以用一个栈保存探索的序列。
并且在该迷宫的行走中,站在一点可以有八个方向选择。
比如如下的迷宫
Enter->0111000000
0001000100
0101100100
0100101100
0100101100
1110101000
0010001011
0010001011
0110101000
0000101100-->EXIT
下面是可能的路径(注意:
从入口到出口可能有多条路径,优先选择的方向不同,路径可能也不一样!
)
Path:
(maze[0][0],maze[1][0],maze[1][1],maze[1][2],maze[2][2],
maze[3][2],maze[3][3],maze[4][3],maze[5][3],maze[6][3],
maze[6][4],maze[6][5],maze[5][5],maze[4][5],maze[3][5],
maze[2][5],maze[2][6],maze[1][6],maze[0][6],maze[0][7],
maze[0][8],maze[1][8],maze[2][8],maze[3][8],maze[4][8],
maze[5][8],maze[5][7],maze[6][7],maze[7][7],maze[8][7],
maze[8][8],maze[8][9],maze[9][9])
Enter->X11100X---X---X0
X---X---X100X1X0
01X11X---X1X0
01X---X1X11X0
010X1X11X0
111X1X1X---X0
001X---X---X1X11
0010001X11
0110101X--X--X
000010110X-->EXIT
1、提示:
(1)数据结构:
✧用二维数组MAZE[m+2][n+2]表示迷宫的结构,数组中的值为1表示是墙,为0表示可以走通。
(用MAZE[m+2][n+2]而不用MAZE[m][n]的原因在于想表示和编写代码的时候简单些,而且让迷宫周围都是墙,防止错误的走出去)
✧用二维数组MARK[m+2][n+2]表示迷宫是否被走过,主要是为了回溯时已经证明走不通的路线就不要再去走了。
(用MARK[m+2][n+2]而不用MARK[m][n]的原因在于想表示和编写代码的时候简单些)
✧二维数据MOVE[8][2]是为了更方便的移动到下一步,改变坐标。
这个二维数组是为了更好的转换坐标(八个方向,从0到7),而且也能避免重复走路
✧用栈保存走过的路径
(2)输出:
✧迷宫布局,用0表示可以走通的地方,用1表示墙
✧如果能走通,则输出路径和路径的长度;若不能走通,则输出提示不能走通
✧带有路径的迷宫布局
2、伪代码
注意:
下面的仅仅是伪代码!
!
!
不能直接做代码使用的,揭示的仅仅是算法本身
Path(MAZE,MARK,m,n,MOVE,STACK)
//AbinarymatrixMAZE(0:
m+1,0:
n+1)holdsthemaze.
//STACK(mn,2)recordsthepath
{
MARK(1,1)=1;
STACK.push((1,1,1));
WhilenotSTACK.empty()
{
(i,j,mov)=STACK.top();
STACK.pop();
Whilemov!
=8
{
g=i+MOVE(mov,1);
h=j+MOVE(mov,2);
ifg=mandh=n
{
reverseprintSTACK;
printi,j;
printm,n;
return;
}
ifMAZE(g,h)=0andMARK(g,h)=0
{
MARK(g,h)=1;
STACK.push((i,j,mov));
i=g;
j=h;
mov=0;
}
elsemov=mov+1;
}
}
print“Nopathhasbeenfound”
}
实验四二叉树的建立和遍历
一、实验目的
1、设计数据结构和算法,实现按层次构造二叉树的算法
2、掌握树的前根序、中根序和后根序遍历算法
二、实验内容
1、实验题目
按层次(从上到下,从左到右的顺序)输入树的结点,如果该结点为空,则用一个特定的值替代(比如0或者.)。
例如下面的图中,输入为ebfad.g..c(当然为了方便输入,也可以用#结束字符串的输入)
要求构造一棵如下的二叉树,当二叉树构造成功后,需要对其进行先序遍历,后序遍历,中序遍历。
2、按层次构造树的两种方法
对于如下的一棵树
输入:
为了构造上图所示的这样一棵二叉树,键盘上输入的顺序如下:
ebfad.g..c#
其中可以看到这是根据层次的一种输入,先输入第一层的结点,然后是第二层的结点,第三层….直到把所有的带信息的结点输入完。
也可以看到,在输入的序列中,有.号,这是代表结点为空。
注意在输入的时候,为空的结点也要这样输入进去。
构造二叉树:
方法一:
用队列
queueA;
queueB;
假如我们把读入的数据放到一个队列中A,用于方便我们的后续处理,那么,在读入后,这个队列中的数据为ebfad.g..c,其中e为队列头存放的元素值(即该指针所指向的节点空间数据域中的值为e),而点代表空指针NULL
把数据读入队列A中的方法如下:
Binary_node*tempNode=newBinary_node();
cin>>tempNode->data;
tempNode->left=NULL;
tempNode->right=NULL;
A.push(tempNode);
为了按层次的构造二叉树,我们还要使用一个队列B,这个队列中保存的是指向要有儿子结点的父亲结点的指针。
下面是这两个队列的变化和树的构造变化情况:
(1)第一步很特殊,首先是树根
Binary_node*pNode=A.front();
A.pop();
B.push(pNode);
A:
bfad.g..c
B:
e
树:
(2)后面的每一步都是从A中取出两个队首,放入B队列尾部(如果为NULL则不放)。
从B中取出队首,队列A中取出的元素正好是B中取出元素的小孩子
Binary_node*pfather=B.front();
B.pop();
Binary_node*pLchild=A.front();//先出来的是左孩子
A.pop();
Binary_node*pRchild=A.front();
A.pop();
pfather->left=pLchild;
pfather->right=pRchild;
//先放入左孩子
if(pLchild!
=NULL)
{
B.push(pLchild);
}
if(pRchild!
=NULL)
{
B.push(pRchild);
}
A:
ad.g..c
B:
bf
树:
(3)
A:
.g..c
B:
fad
树:
(4)
A:
..c
B:
adg
树:
(5)
A:
c
B:
dg
树:
(6)
A:
空(当队列A为空的时候整个算法结束,树构造成功)
B:
g
树:
第二种方法:
给树的结点按层次从上到下,从左到右编号(编号从1开始,空节点也要编号),利用父亲跟小孩的编号的关系来编写代码。
即节点的编号除以2,得到的商代表这该节点父亲节点的编号,得到的余数为0,则表示该节点为父亲的左孩子,若得到的余数为1,则表示该节点为父亲的右孩子。
实现方法可以为,
(1)为每个节点分配空间;
(2)定义一个一维数组,该数组中存放的元素为指向节点的指针,将每个分配好空间的节点的指针放入该一维数组中(3)逐一访问节点,确定节点的父子关系。
当除了根节点外,每个节点的父亲都确定后,这颗二叉树就构造好了。
两种方法分析:
用队列的方法,实现有点难度,但是灵活。
数组的方法,实现比较简单,但是有局限性。
3、程序简单模板
#include
usingnamespacestd;
structBinary_node
{
chardata;//datamembers
Binary_node*left;
Binary_node*right;
};
classBinary_tree
{
public:
Binary_tree();//构造函数,里面将root=NULL;
//通过队列中保存的数据,构造树
voidbuildTree(void);
//遍历二叉树
voidpreorder(void);
voidinorder(void);
voidpostorder(void);
private:
//遍历树的辅助函数
voidrecursive_inorder(Binary_node*root);
voidrecursive_preorder(Binary_node*root);
voidrecursive_postorder(Binary_node*root);
Binary_node*root;//树根
};
这里给出一个C++的类,仅供参考,自己写代码的时候并不要求一定如此。
附录:
STL中队列的使用
注:
队列,这里也不要求大家再去自己实现一个有关队列的类,直接用标准模板库(STL)中的队列就可以了。
需要#include
STL中的queue,里面的一些成员函数如下(这里仅仅描述也许会用到的几个,具体不明白可以查找msdn,搜索queueclass):
front:
Returnsareferencetothefirstelementatthefrontofthequeue.
pop:
Removesanelementfromthefrontofthequeue
push:
Addsanelementtothebackofthequeue
empty:
Testsifthequeueisempty