1、1J I A N G S U U N I V E R S I T Y数据结构课程设计实验报告以邻接链表的方式确定一个无向网学院名称: 计算机科学与通信工程专业班级: 软件 1202 学生姓名: 吕中剑 学生学号: 3120608053指导老师: 王新胜 完成日期: 2014 年 1 月 13 日2目录一、需求分析 -11运行环境 -12程序所实现的功能 -13程序的输入 -14程序的输出 -15测试数据 -1二、设计说明 -21算法设计的思想 -22主要的数据结构设计说明 -43程序的主要流程图 -44程序的主要模块 -45程序的主要函数及其伪代码说明 -5三、上机结果及体会 -51上机过程中
2、出现的问题及其解决方案。 -52程序中可以改进的地方说明 -5四、实验源程序 -51无向网络图的边节点类定义 -52无向网络图的顶点节点类定义 -63最小生成树的类定义 -64最小生成树边节点的类定义 -75普里姆算法中的辅助数组 CLOSEARC的类定义 -86克鲁斯卡尔算法中最小堆的类定义 -87克鲁斯卡尔算法中并查集的类定义 -108无向网络图的类定义 -129主函数调试 -19五、输出结果显示 -20六、收获及体会 -21七、参考文献 -22一、需求分析1运行环境Microsoft Visual Studio 20052程序所实现的功能(1)建立并显示图的邻接矩阵;(2)广度优先遍历该
3、图,显示遍历结果;(3)用普里姆算法构造该图的最小生成树,显示构造过程;(4)用克鲁斯卡尔算法构造该图的最小生成树,显示构造过程。3程序的输入(1)输入顶点数,及各顶点信息(数据格式为 String);(2)输入边数以及各边的依附顶点及其权值(依附顶点数据格式为 String,权值数据格式为 int)。4程序的输出(1)输出图的邻接矩阵;(2)输出广度优先遍历结果;(3)输出普里姆算法构造最小生成树的过程;(4)输出克鲁斯卡尔算法构造最小生成树的过程。5测试数据(1)顶点个数:6(2)各个顶点为:A B C D E F (3)边数:9(4)输入所有的边(格式为“顶点 顶点 权值” )为:A B
4、 34 A C 46 A F 19 B E 12 C D 17 C F 25D E 38 D F 25 E F 26二、设计说明1算法设计的思想建立图类,建立相关成员函数。最后在主函数中实现。具体成员函数的实现请参看源程序。(1) 邻接链表邻接链表是一种链式存储结构。在邻接链表中,对图中每个顶点建立一个单链表,第 i 个单链表中的结点表示依附于顶点 Vi 的边(对有向图是以顶点 Vi 为尾的弧) 。每个结点由 3 个域组成,其中邻接点域指示与顶点 Vi 邻接的点在图中的位置,链域指示下一条边或弧的结点;数据域存储和边或弧相关的信息,如权值等。所以一开始必须先定义邻接链表的边结点类型以及邻接链表
5、类型,并对邻接链表进行初始化,然后根据所输入的相关信息,包括图的顶点数、边数、是否为有向(此处默认为无向) ,以及各条边的起点与终点及权值,建立图的邻接链表。(2) 邻接矩阵图的邻接矩阵存储表示即是数组存储表示,在邻接矩阵中,我们定义两个数组分别存储数据元素的信息和数据元素之间的关系(边或弧)的信息,以二维数组表示有 n 个顶点的图时,需存放 n 个顶点信息和 n 的平方个弧信息的存储量。借助于邻接矩阵容易判定任意两个顶点之间是否有边或弧相连,并容易求得各个顶点的度。故在建立邻接矩阵之前,必须先定义顶点关系类型和相关指针指示弧的信息。(3) 广度优先遍历假设从图总某顶点出发,在访问了 v 之后
6、依次访问 v 的各个未曾访问到的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的邻接点”先于“后被访问的邻接点”被访问,直至图中所有已被访问的邻接点都被访问到。若此时图中尚有顶点未被访问到,则另选图中一个未曾被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问到为止。换句话说,广度优先搜索遍历图的过程是以 v 为起始点,由近及远,依次访问饿 v 有路径相通且路径长度为 1,2.的顶点。和深度优先搜索类似,在遍历过程中也需要一个访问标志数组。并且,为了顺次访问路径长度为 2,3的顶点,需附设队列以存储已被访问的路径长度为 1,2.的顶点。所以要实现算法必须先建立一
7、个元素类型为整形的空队列,并定义队首与队尾指针,同时也要定义一个标志数组以标记结点是否被访问。同样,也是从初始点出发开始访问,访问初始点,标志其已被访问,并将其入队。当队列非空时进行循环处理。当结点被访问时对其进行标志,并入队列。通过 while()循环,并以是否被访问为控制条件,访问所有结点,完成图的广度优先遍历。(4) 普里姆算法假设 N=(V,E)是连通网,TE 是 N 上最小生成数中边的集合。算法从 U=U0,TE=开始,重复执行如下操作;在所有的边(u,v)中找一条代价最小的边(u0,v0)并入集合 TE,同时 V0 并入 U,直到U=V 为止。此时 TE 中必有 n-1 条边,则,
8、T=(V,TE)为 N 的最小生成树。(5) 克鲁斯卡尔算法假设连通网 N=(V,E),则令最小生成树的初始状态为只有 n 个顶点且无边的非连通图 T=(V,),图中每个顶点自成一个连通分量。在 E 中选择代价最小的边,若该边依附的顶点落在 T 中不同的连通分量上,则将此边加入到 T 中,否则舍去此边而选择下一条代价最小的边。依次类推,直到 T 中的所有顶点都在同一个连通分量上为止。2主要的数据结构设计说明图的邻接矩阵、邻接表的建立。图的广度优先遍历以及分别用普里姆算法和克鲁斯卡尔算法构造最小生成树。3程序的主要流程图4 程序的主要模块用邻接链表的方式确定一个无向图,以邻接矩阵的方式输出,再进
9、行图的广度优先遍历以及分别用普里姆算法和克鲁斯卡尔算法构造最小生无向网邻接表方式确定输出邻接矩阵普里姆算法克鲁斯卡尔算法广度优先遍历成树并输出。5程序的主要函数及其伪代码说明 (不需要完整的代码)Graph();构造函数,构造无向网络图(以邻接表方式构造)void BuildAndDisplay();邻接矩阵的建立和输出void BFTraverse();广度优先遍历void Prim(MinSpanTree普里姆算法void Kruskal(MinSpanTree克鲁斯卡尔算法三、上机结果及体会1上机过程中出现的问题及其解决方案。问题:编译时出现许多错误。解决方案:有很多是细节错误,简单改正
10、下即可。但还有一些是算法上的 错误,需要断点逐步调试找到错误。找错误时要耐心和细心。2程序中可以改进的地方说明(1)程序中的数据要以正确方式输入,否则会出错。可以添加语句实现对不正确数据的重新输入。(2)该程序只能构造一个网络无向图。可以在主程序中设计循环来选择是 否继续构造无向图。四、实验源程序1无向网络图的边节点类定义#ifndef ARCNODE#define ARCNODEtemplate class ArcNode/无向网络图的边节点类定义public:ArcNode* nextarc;int vex;A weight;/构造边节点ArcNode();/构造边节点ArcNode(in
11、t v,A w)vex=v;weight=w;nextarc=NULL;# endif2无向网络图的顶点节点类定义#ifndef VERTEXNODE#define VERTEXNODEtemplate class VertexNode/无向网络图的顶点节点类定义public:ArcNode* first;V data;/构造函数VertexNode();/构造函数VertexNode(V d)data=d;first=NULL;# endif3最小生成树的类定义#ifndef MINSPANTREE#define MINSPANTREE#include“MSTArcNode.h“const
12、int MaxNumArcs=20;/最大边数量templateclass MinSpanTree/最小生成树的类定义protected:MSTArcNode* arctable;/存放边的数组int CurrentNumArcs;/当前边数public:MinSpanTree()CurrentNumArcs=0;arctable=new MSTArcNodeMaxNumArcs;void Insert(MSTArcNode/边的依附顶点A weight;/边的权值MSTArcNode(A w=0)weight=w;# endif5 普里姆算法中的辅助数组 closearc的类定义#ifnde
13、f CLOSEARCTYPE#define CLOSEARCTYPEtemplateclass CloseArcType/普里姆算法中的辅助数组closearc的类定义public:A lowweight;/边的权值int nearvex;# endif6克鲁斯卡尔算法中最小堆的类定义#ifndef MINHEAP#define MINHEAP#includetemplateclass MinHeap/最小堆的类定义private:T*heapArr;/存放堆中数据元素的数组int heapCurrentSize;/当前数据元素个数int heapMaxSize;/堆中数据元素最大数目int
14、IsFull() const/判断堆是否满。满返回,否则返回return heapCurrentSize=heapMaxSize;int IsEmpty() const/判断堆是否空。空返回,否则返回return heapCurrentSize=0;void FilterUp(int p)/从p开始向上调整。使序列成为堆int j=p,i;T temp=heapArrj;i=(j-1)/2;while(j0)if(heapArri.weightheapArrj+1.weight)j+;/在左右孩子中选权值小的if(temp.weightclass UFSets;templateclass Tr
15、eeNode/定义树的双亲表示节点类friend class UFSets;private:T Tdata;/数据域int parent;/双亲域;templateclass UFSets/并查集的类定义private:TreeNode*sets;/存储集合元素的数组int size;/集合大小int Order(T d)/定位函数,确定数据元素d在数组中的位置int p=0;while(psize;for(int i=0;i#include#include#include“ArcNode.h“#include“VertexNode.h“#include“MinSpanTree.h“#incl
16、ude“CloseArcType.h“#include“UFSets.h“#include“MinHeap.h“using namespace std;const int MaxVertexes=20;const int MaxNum=999999;/表示无穷template class Graph/无向网络图类定义private:A arcsMaxVertexesMaxVertexes;/邻接矩阵VertexNode *VertexesTable;int CurrentNumVertexes;int CurrentNumArcs;/取 v在顶点节点表中的位置int GetVertexPos(
17、const ViMaxVertexes;int e;V tail,head;A w;coutnum;while(numMaxVertexes)coutnum;for(int i=1;id;VertexesTablei-1.data=d;VertexesTablei-1.first=NULL;CurrentNumVertexes+;coute;while(enum*(num-1)/2)coute;for(int i=1;itailheadw;/输入顶点和权值InsertArc(tail,head,w);/析构函数Graph()for(int i=0;i*p=VertexesTablei.firs
18、t;while(p)/逐条边释放VertexesTablei.first=p-nextarc;delete p;p=VertexesTablei.first;delete VertexesTable;/释放顶点表/取得点的信息V GetValue(int v)if(v=0if(VertexesTablet.first=NULL)VertexesTablet.first=p;elseArcNode*q=VertexesTablet.first;if(q-nextarc)q=q-nextarc;q-nextarc=p;p=new ArcNode(t,w);if(VertexesTableh.fir
19、st=NULL)VertexesTableh.first=p;elseArcNode*q=VertexesTableh.first;if(q-nextarc)q=q-nextarc;q-nextarc=p;CurrentNumArcs+;/获取 v的第一个邻接点int GetFirst(int v)if(VertexesTablev.first)return VertexesTablev.first-vex;return -1;/获取 v在w后的一个邻接点int GetNext(int v,int w)for(ArcNode*p=VertexesTablev.first;p;p=p-nexta
20、rc)if(p-vex=wreturn -1;/建立邻接矩阵并显示void BuildAndDisplay()int num=GetVertexesNum();for(int i=0;i*p=VertexesTablei.first;while(p)int n=p-vex;A w=p-weight;arcsin=w;p=p-nextarc;for(int i=0;i/无向网络图的顶点总数CloseArcType * closearc=new CloseArcTypen;/辅助数组MSTArcNode e;/最小生成树边节点的辅助变量A min;int v,i,j;for(i=1;iMSTArc
21、Node e;/边结点辅助单元MinHeaph(CurrentNumArcs);/定义边的最小堆int n=GetVertexesNum();/无向网络图的顶点总数int a=GetArcsNum();/无向网络图的边总数UFSets f(VertexesTable,n);/定义并查集f并初始化for(u=0;u#include#include“Graph.h“using namespace std;void main()cout G;cout T1;G.Prim(T1);coutT2;G.Kruskal(T2);system(“pause“);五、输出结果显示一些说明,本程序的健壮性还不是很
22、好,比如说在数据的录入的时候,用户需要小心的正确输入顶点和权值。六、收获及体会这次课程设计虽然时间不长,但收获很大。这次做的是邻接表的题目,在平时上机时已经有所接触,所以完成得比较快。不过和别人对比了下,发现自己还是有很多地方不足的。比如说显示界面做得就不够好,并且每次运行只能执行一次程序等等。但同时也学到了不少东西,首先是算法,这是十分重要的,每个要实现的功能有自己的算法,并且这算法并不唯一,因此需要找到最好的算法。其次有了算法后,编写代码时会遇到种种错误,这需要自己找出错误,并且这过程中还会百度自己的错误在什么地方,总之这是需要耐心及细心才能完成的。最后调试时可能还会遇到其他错误,这就是自己写的算法中的地方出错或是别的一些细节上的错误,这也需要耐心去寻找错误并改正。在今后必然还会遇到更加难和复杂的项目,总之要先了解大致的情况,将整个项目分成若干个子项目,并且了解每个子项目需要的算法,然后一个个完成子项目并调试,最后再整合起来。总之要学习的东西还有很多,因此还要继续努力。七、参考文献缪淮扣,顾训穰,沈俊.数据结构C实现.科学出版社,2011年修订