1、数据结构(C 语言版) 实验报告专业 班级 学号 姓名 实验 1实验题目:单链表的插入和删除实验目的:了解和掌握线性表的逻辑结构和链式存储结构,掌握单链表的基本算法及相关的时间性能分析。实验要求:建立一个数据域定义为字符串的单链表,在链表中不允许有重复的字符串;根据输入的字符串,先找到相应的结点,后删除之。实验主要步骤:1、分析、理解给出的示例程序。2、调试程序,并设计输入数据(如:bat,cat ,eat,fat,hat,jat,lat ,mat,#) ,测试程序的如下功能:不允许重复字符串的插入;根据输入的字符串,找到相应的结点并删除。3、修改程序:(1) 增加插入结点的功能。(2) 将建
2、立链表的方法改为头插入法。程序代码:#include“stdio.h“#include“string.h“#include“stdlib.h“#include“ctype.h“typedef struct node /定义结点char data10; /结点的数据域为字符串struct node *next; /结点的指针域ListNode;typedef ListNode * LinkList; / 自定义 LinkList 单链表类型LinkList CreatListR1(); /函数,用尾插入法建立带头结点的单链表LinkList CreatList(void); /函数,用头插入法建
3、立带头结点的单链表ListNode *LocateNode(); /函数,按值查找结点void DeleteList(); /函数,删除指定值的结点void printlist(); /函数,打印链表中的所有值void DeleteAll(); /函数,删除所有结点,释放内存ListNode * AddNode(); /修改程序:增加节点。用头插法,返回头指针/=主函数=void main()char ch10,num5;LinkList head;head=CreatList(); /用头插入法建立单链表,返回头指针printlist(head); /遍历链表输出其值printf(“ Del
4、ete node (y/n):“); /输入“y“或“n“ 去选择是否删除结点scanf(“%s“,num);if(strcmp(num,“y“)=0 | strcmp(num,“Y“)=0)printf(“Please input Delete_data:“);scanf(“%s“,ch); /输入要删除的字符串DeleteList(head,ch);printlist(head);printf(“ Add node ? (y/n):“); /输入“y“或“n“ 去选择是否增加结点scanf(“%s“,num);if(strcmp(num,“y“)=0 | strcmp(num,“Y“)=0
5、)head=AddNode(head);printlist(head);DeleteAll(head); /删除所有结点,释放内存/=用尾插入法建立带头结点的单链表=LinkList CreatListR1(void)char ch10;LinkList head=(LinkList)malloc(sizeof(ListNode); /生成头结点ListNode *s,*r,*pp;r=head;r-next=NULL;printf(“Input # to end “); /输入“#“代表输入结束printf(“nPlease input Node_data:“);scanf(“%s“,ch)
6、; /输入各结点的字符串while(strcmp(ch,“#“)!=0) pp=LocateNode(head,ch); /按值查找结点,返回结点指针if(pp=NULL) /没有重复的字符串,插入到链表中s=(ListNode *)malloc(sizeof(ListNode);strcpy(s-data,ch);r-next=s;r=s;r-next=NULL;printf(“Input # to end “);printf(“Please input Node_data:“);scanf(“%s“,ch);return head; /返回头指针/=用头插入法建立带头结点的单链表=Link
7、List CreatList(void)char ch100;LinkList head,p;head=(LinkList)malloc(sizeof(ListNode); head-next=NULL;while(1)printf(“Input # to end “); printf(“Please input Node_data:“);scanf(“%s“,ch); if(strcmp(ch,“#“) if(LocateNode(head,ch)=NULL) strcpy(head-data,ch);p=(LinkList)malloc(sizeof(ListNode); p-next=h
8、ead;head=p;else break;return head; /=按值查找结点,找到则返回该结点的位置,否则返回 NULL=ListNode *LocateNode(LinkList head, char *key)ListNode *p=head-next; /从开始结点比较while(p!=NULL /扫描下一个结点return p; /若 p=NULL 则查找失败,否则 p 指向找到的值为 key 的结点/=修改程序:增加节点=ListNode * AddNode(LinkList head)char ch10;ListNode *s,*pp;printf(“nPlease in
9、put a New Node_data:“);scanf(“%s“,ch); /输入各结点的字符串pp=LocateNode(head,ch); /按值查找结点,返回结点指针printf(“ok2n“);if(pp=NULL) /没有重复的字符串,插入到链表中s=(ListNode *)malloc(sizeof(ListNode);strcpy(s-data,ch);printf(“ok3n“);s-next=head-next;head-next=s;return head;/=删除带头结点的单链表中的指定结点=void DeleteList(LinkList head,char *key
10、)ListNode *p,*r,*q=head;p=LocateNode(head,key); /按 key 值查找结点的if(p=NULL ) /若没有找到结点,退出printf(“position error“);exit(0);while(q-next!=p) /p 为要删除的结点,q 为 p 的前结点q=q-next;r=q-next;q-next=r-next;free(r); /释放结点/=打印链表=void printlist(LinkList head)ListNode *p=head-next; /从开始结点打印while(p)printf(“%s, “,p-data);p=
11、p-next;printf(“n“);/=删除所有结点,释放空间=void DeleteAll(LinkList head)ListNode *p=head,*r;while(p-next)r=p-next;free(p);p=r;free(p);实验结果:Input # to end Please input Node_data:batInput # to end Please input Node_data:catInput # to end Please input Node_data:eatInput # to end Please input Node_data:fatInput #
12、 to end Please input Node_data:hatInput # to end Please input Node_data:jatInput # to end Please input Node_data:latInput # to end Please input Node_data:matInput # to end Please input Node_data:#mat, lat, jat, hat, fat, eat, cat, bat,Delete node (y/n):yPlease input Delete_data:hatmat, lat, jat, fat
13、, eat, cat, bat,Insert node (y/n):yPlease input Insert_data:putposition :5mat, lat, jat, fat, eat, put, cat, bat,请按任意键继续. . .示意图:lat jat hat fat eat cat batmatNULLheadlat jathatfat eat cat batmatheadlat jat fat eatputcat batmatheadNULLNULL心得体会:本次实验使我们对链表的实质了解更加明确了,对链表的一些基本操作也更加熟练了。另外实验指导书上给出的代码是有一些问
14、题的,这使我们认识到实验过程中不能想当然的直接编译执行,应当在阅读并完全理解代码的基础上再执行,这才是实验的意义所在。实验 2实验题目:二叉树操作设计和实现实验目的:掌握二叉树的定义、性质及存储方式,各种遍历算法。实验要求:采用二叉树链表作为存储结构,完成二叉树的建立,先序、中序和后序以及按层次遍历的操作,求所有叶子及结点总数的操作。实验主要步骤:1、分析、理解程序。2、调试程序,设计一棵二叉树,输入完全二叉树的先序序列,用#代表虚结点(空指针) ,如 ABD#CE#F#,建立二叉树,求出先序、中序和后序以及按层次遍历序列,求所有叶子及结点总数。实验代码#include“stdio.h“#in
15、clude“stdlib.h“#include“string.h“#define Max 20 /结点的最大个数typedef struct nodechar data;struct node *lchild,*rchild;BinTNode; /自定义二叉树的结点类型typedef BinTNode *BinTree; /定义二叉树的指针int NodeNum,leaf; /NodeNum 为结点数,leaf 为叶子数/=基于先序遍历算法创建二叉树=/=要求输入先序序列,其中加入虚结点“#“以示空指针的位置=BinTree CreatBinTree(void)BinTree T;char c
16、h;if(ch=getchar()=#)return(NULL); /读入#,返回空指针else T= (BinTNode *)malloc(sizeof(BinTNode); /生成结点T-data=ch;T-lchild=CreatBinTree(); /构造左子树T-rchild=CreatBinTree(); /构造右子树return(T);/=NLR 先序遍历=void Preorder(BinTree T)if(T) printf(“%c“,T-data); /访问结点Preorder(T-lchild); /先序遍历左子树Preorder(T-rchild); /先序遍历右子树/
17、=LNR 中序遍历=void Inorder(BinTree T)if(T) Inorder(T-lchild); /中序遍历左子树printf(“%c“,T-data); /访问结点Inorder(T-rchild); /中序遍历右子树/=LRN 后序遍历=void Postorder(BinTree T)if(T) Postorder(T-lchild); /后序遍历左子树Postorder(T-rchild); /后序遍历右子树printf(“%c“,T-data); /访问结点/=采用后序遍历求二叉树的深度、结点数及叶子数的递归算法=int TreeDepth(BinTree T)in
18、t hl,hr,max;if(T)hl=TreeDepth(T-lchild); /求左深度hr=TreeDepth(T-rchild); /求右深度max=hlhr? hl:hr; /取左右深度的最大值NodeNum=NodeNum+1; /求结点数if(hl=0 /若左右深度为 0,即为叶子。return(max+1);else return(0);/=利用“先进先出“(FIFO)队列,按层次遍历二叉树=void Levelorder(BinTree T)int front=0,rear=1;BinTNode *cqMax,*p; /定义结点的指针数组 cqcq1=T; /根入队while
19、(front!=rear) front=(front+1)%NodeNum;p=cqfront; /出队printf(“%c“,p-data); /出队,输出结点的值 if(p-lchild!=NULL)rear=(rear+1)%NodeNum;cqrear=p-lchild; /左子树入队if(p-rchild!=NULL)rear=(rear+1)%NodeNum;cqrear=p-rchild; /右子树入队/=数叶子节点个数 =int countleaf(BinTree T)int hl,hr;if(T)hl=countleaf(T-lchild);hr=countleaf(T-rc
20、hild);if(hl=0else return hl+hr;else return 0;/=主函数=void main()BinTree root;char i;int depth;printf(“n“);printf(“Creat Bin_Tree; Input preorder:“); /输入完全二叉树的先序序列,/ 用#代表虚结点,如 ABD#CE#F#root=CreatBinTree(); /创建二叉树,返回根结点do /从菜单中选择遍历方式,输入序号。printf(“t* select *n“);printf(“t1: Preorder Traversaln“); printf(
21、“t2: Iorder Traversaln“);printf(“t3: Postorder traversaln“);printf(“t4: PostTreeDepth,Node number,Leaf numbern“);printf(“t5: Level Depthn“); /按层次遍历之前,先选择 4,求出该树的结点数。printf(“t0: Exitn“);printf(“t*n“);fflush(stdin);scanf(“%c“, /输入菜单序号(0-5)switch (i-0)case 1: printf(“Print Bin_tree Preorder: “);Preorde
22、r(root); /先序遍历break;case 2: printf(“Print Bin_Tree Inorder: “);Inorder(root); /中序遍历break;case 3: printf(“Print Bin_Tree Postorder: “);Postorder(root); /后序遍历break;case 4: depth=TreeDepth(root); /求树的深度及叶子数printf(“BinTree Depth=%d BinTree Node number=%d“,depth,NodeNum);printf(“ BinTree Leaf number=%d“,
23、countleaf(root);break;case 5: printf(“LevePrint Bin_Tree: “);Levelorder(root); /按层次遍历break;default: exit(1);printf(“n“); while(i!=0); 实验结果:Creat Bin_Tree; Input preorder:ABD#CE#F# * select * 1: Preorder Traversal 2: Iorder Traversal 3: Postorder traversal 4: PostTreeDepth,Node number,Leaf number 5:
24、Level Depth 0: Exit * 1 Print Bin_tree Preorder: ABDCEF 2 Print Bin_Tree Inorder: DBAECF 3 Print Bin_Tree Postorder: DBEFCA 4 BinTree Depth=3 BinTree Node number=6 BinTree Leaf number=3 5 LevePrint Bin_Tree: ABCDEF 0 Press any key to continue 二叉树示意图ABFEDC心得体会:这次实验加深了我对二叉树的印象,尤其是对二叉树的各种遍历操作有了一定的了解。同时
25、认识到,在设计程序时辅以图形化的描述是非常有用处的。实验 3实验题目:图的遍历操作实验目的:掌握有向图和无向图的概念;掌握邻接矩阵和邻接链表建立图的存储结构;掌握DFS 及 BFS 对图的遍历操作;了解图结构在人工智能、工程等领域的广泛应用。实验要求:采用邻接矩阵和邻接链表作为图的存储结构,完成有向图和无向图的 DFS 和 BFS操作。实验主要步骤:设计一个有向图和一个无向图,任选一种存储结构,完成有向图和无向图的 DFS(深度优先遍历)和 BFS(广度优先遍历)的操作。1邻接矩阵作为存储结构#include“stdio.h“#include“stdlib.h“#define MaxVerte
26、xNum 100 /定义最大顶点数typedef structchar vexsMaxVertexNum; /顶点表int edgesMaxVertexNumMaxVertexNum; /邻接矩阵,可看作边表int n,e; /图中的顶点数 n 和边数 eMGraph; /用邻接矩阵表示的图的类型/=建立邻接矩阵=void CreatMGraph(MGraph *G)int i,j,k;char a;printf(“Input VertexNum(n) and EdgesNum(e): “);scanf(“%d,%d“, /输入顶点数和边数scanf(“%c“, printf(“Input V
27、ertex string:“);for(i=0;in;i+) scanf(“%c“,G-vexsi=a; /读入顶点信息,建立顶点表for(i=0;in;i+)for(j=0;jn;j+)G-edgesij=0; /初始化邻接矩阵printf(“Input edges,Creat Adjacency Matrixn“);for(k=0;ke;k+) /读入 e 条边,建立邻接矩阵 scanf(“%d%d“, /输入边(Vi,Vj)的顶点序号G-edgesij=1; G-edgesji=1; /若为无向图,矩阵为对称矩阵;若建立有向图,去掉该条语句 /=定义标志向量,为全局变量=typedef
28、enumFALSE,TRUE Boolean;Boolean visitedMaxVertexNum;/=DFS:深度优先遍历的递归算法=void DFSM(MGraph *G,int i) /以 Vi 为出发点对邻接矩阵表示的图 G 进行 DFS 搜索,邻接矩阵是 0,1 矩阵int j;printf(“%c“,G-vexsi); /访问顶点 Vivisitedi=TRUE; /置已访问标志for(j=0;jn;j+) /依次搜索 Vi 的邻接点if(G-edgesij=1 /(Vi,Vj)E,且 Vj 未访问过,故 Vj 为新出发点void DFS(MGraph *G) int i;for
29、(i=0;in;i+)visitedi=FALSE; /标志向量初始化for(i=0;in;i+)if(!visitedi) /Vi 未访问过DFSM(G,i); /以 Vi 为源点开始 DFS 搜索/=BFS:广度优先遍历=void BFS(MGraph *G,int k) /以 Vk 为源点对用邻接矩阵表示的图 G 进行广度优先搜索int i,j,f=0,r=0;int cqMaxVertexNum; /定义队列 for(i=0;in;i+)visitedi=FALSE; /标志向量初始化for(i=0;in;i+)cqi=-1; /队列初始化printf(“%c“,G-vexsk); /
30、访问源点 Vkvisitedk=TRUE;cqr=k; /Vk 已访问,将其入队。注意,实际上是将其序号入队while(cqf!=-1) /队非空则执行i=cqf; f=f+1; /Vf 出队for(j=0;jn;j+) /依次 Vi 的邻接点 Vjif(G-edgesij=1 /访问 Vjvisitedj=TRUE;r=r+1; cqr=j; /访问过 Vj 入队/=main=void main()int i;MGraph *G;G=(MGraph *)malloc(sizeof(MGraph); /为图 G 申请内存空间CreatMGraph(G); /建立邻接矩阵printf(“Prin
31、t Graph DFS: “);DFS(G); /深度优先遍历printf(“n“);printf(“Print Graph BFS: “);BFS(G,3); /以序号为 3 的顶点开始广度优先遍历printf(“n“);2 邻接链表作为存储结构#include“stdio.h“#include“stdlib.h“#define MaxVertexNum 50 /定义最大顶点数typedef struct node /边表结点int adjvex; /邻接点域struct node *next; /链域EdgeNode;typedef struct vnode /顶点表结点char vert
32、ex; /顶点域EdgeNode *firstedge; /边表头指针VertexNode;typedef VertexNode AdjListMaxVertexNum; /AdjList 是邻接表类型typedef struct AdjList adjlist; /邻接表int n,e; /图中当前顶点数和边数 ALGraph; /图类型/=建立图的邻接表=void CreatALGraph(ALGraph *G)int i,j,k;char a;EdgeNode *s; /定义边表结点printf(“Input VertexNum(n) and EdgesNum(e): “);scanf(
33、“%d,%d“, /读入顶点数和边数scanf(“%c“,printf(“Input Vertex string:“);for(i=0;in;i+) /建立边表scanf(“%c“,G-adjlisti.vertex=a; /读入顶点信息G-adjlisti.firstedge=NULL; /边表置为空表printf(“Input edges,Creat Adjacency Listn“);for(k=0;ke;k+) /建立边表 scanf(“%d%d“, /读入边(Vi,Vj)的顶点对序号s=(EdgeNode *)malloc(sizeof(EdgeNode); /生成边表结点s-adj
34、vex=j; /邻接点序号为 js-next=G-adjlisti.firstedge;G-adjlisti.firstedge=s; /将新结点*S 插入顶点 Vi 的边表头部s=(EdgeNode *)malloc(sizeof(EdgeNode); s-adjvex=i; /邻接点序号为 is-next=G-adjlistj.firstedge; G-adjlistj.firstedge=s; /将新结点*S 插入顶点 Vj 的边表头部/=定义标志向量,为全局变量=typedef enumFALSE,TRUE Boolean;Boolean visitedMaxVertexNum;/=D
35、FS:深度优先遍历的递归算法=void DFSM(ALGraph *G,int i) /以 Vi 为出发点对邻接链表表示的图 G 进行 DFS 搜索EdgeNode *p;printf(“%c“,G-adjlisti.vertex); /访问顶点 Vivisitedi=TRUE; /标记 Vi 已访问p=G-adjlisti.firstedge; /取 Vi 边表的头指针while(p) /依次搜索 Vi 的邻接点 Vj,这里 j=p-adjvexif(! visitedp-adjvex) /若 Vj 尚未被访问DFSM(G,p-adjvex); /则以 Vj 为出发点向纵深搜索p=p-nex
36、t; /找 Vi 的下一个邻接点void DFS(ALGraph *G)int i;for(i=0;in;i+)visitedi=FALSE; /标志向量初始化for(i=0;in;i+)if(!visitedi) /Vi 未访问过DFSM(G,i); /以 Vi 为源点开始 DFS 搜索/=BFS:广度优先遍历=void BFS(ALGraph *G,int k) /以 Vk 为源点对用邻接链表表示的图 G 进行广度优先搜索int i,f=0,r=0;EdgeNode *p;int cqMaxVertexNum; /定义 FIFO 队列for(i=0;in;i+)visitedi=FALSE
37、; /标志向量初始化for(i=0;in;i+)cqi=-1; /初始化标志向量printf(“%c“,G-adjlistk.vertex); /访问源点 Vkvisitedk=TRUE;cqr=k; /Vk 已访问,将其入队。注意,实际上是将其序号入队 while(cqf!=-1) 队列非空则执行i=cqf; f=f+1; /Vi 出队p=G-adjlisti.firstedge; /取 Vi 的边表头指针while(p) /依次搜索 Vi 的邻接点 Vj(令 p-adjvex=j)if(!visitedp-adjvex) /若 Vj 未访问过printf(“%c“,G-adjlistp-a
38、djvex.vertex); /访问 Vjvisitedp-adjvex=TRUE;r=r+1; cqr=p-adjvex; /访问过的 Vj 入队p=p-next; /找 Vi 的下一个邻接点/endwhile/=主函数=void main()int i;ALGraph *G;G=(ALGraph *)malloc(sizeof(ALGraph);CreatALGraph(G);printf(“Print Graph DFS: “);DFS(G);printf(“n“);printf(“Print Graph BFS: “);BFS(G,3);printf(“n“);实验结果:1. 邻接矩阵
39、作为存储结构执行顺序:Input VertexNum(n) and EdgesNum(e): 8,9Input Vertex string: 01234567Input edges,Creat Adjacency Matrix0 10 21 31 42 52 63 74 75 6Print Graph DFS: 01374256Print Graph BFS: 317042562. 邻接链表作为存储结构执行顺序:Input VertexNum(n) and EdgesNum(e): 8,9Input Vertex string: 01234567Input edges,Creat Adjace
40、ncy List0 10 21 31 42 52 63 74 75 6Print Graph DFS: 02651473Print Graph BFS: 37140265心得体会:这次实验较以前的实验难度加大,必须先理解深度优先和广度优先两种遍历思路,和数据结构中队列的基本操作,才能看懂理解代码。同时图这种数据结构对抽象的能力要求非常高,代码不容易看懂,排错也比较麻烦,应该多加练习,才能掌握。V6V4 V5V7V2V3V1V0VoV6V4 V5V7V2V3V1V0Vo实验 4 实验题目:排序实验目的:掌握各种排序方法的基本思想、排序过程、算法实现,能进行时间和空间性能的分析,根据实际问题的特点
41、和要求选择合适的排序方法。实验要求:实现直接排序、冒泡、直接选择、快速、堆、归并排序算法。比较各种算法的运行速度。实验主要步骤:实验代码#include“stdio.h“#include“stdlib.h“#define Max 100 /假设文件长度typedef struct /定义记录类型int key; /关键字项RecType;typedef RecType SeqListMax+1; /SeqList 为顺序表,表中第 0 个元素作为哨兵int n; /顺序表实际的长度/=直接插入排序法=void InsertSort(SeqList R) /对顺序表 R 中的记录 R1n按递增序
42、进行插入排序int i,j;for(i=2;i=i;j-) /对当前无序区 Rin 自下向上扫描if(Rj+1.key=pivot.key) /基准记录 pivot 相当与在位置 i 上j-; /从右向左扫描,查找第一个关键字小于 pivot.key 的记录 Rjif(i pivot.key,则Rj-=Ri; /交换 Ri和 Rj,交换后 j 指针减 1Ri=pivot; /此时,i=j,基准记录已被最后定位return i; /返回基准记录的位置/2.=快速排序=void QuickSort(SeqList R,int low,int high) /Rlowhigh快速排序int pivotpos; /划分后基准记录的位置if(lowhigh,则表示 Rlow是叶子,调整结束;否则先令 large 指向 Rlow的左孩子if(large=Rlarge.key) /temp 始终对应 Rlowbreak; /当前调整结点不小于其孩子结点的关键字,结束调整Rlow=Rlarge; /相当于交换了 Rlow和 Rlargelow=large; /令 low 指向新的调整结点,相当于 temp 已筛下到 large 的位置Rlow=temp; /将被调整结点放入最终位置上/=构造大根堆=void BuildHeap(Seq