数据结构课设之图文档格式.docx
《数据结构课设之图文档格式.docx》由会员分享,可在线阅读,更多相关《数据结构课设之图文档格式.docx(66页珍藏版)》请在冰豆网上搜索。
![数据结构课设之图文档格式.docx](https://file1.bdocx.com/fileroot1/2022-12/11/86c37a8d-698c-45ca-a481-1c7289888738/86c37a8d-698c-45ca-a481-1c72898887381.gif)
3求最小生成树
3.有向图的基本操作及应用
1创建有向图的邻接矩阵
2创建有向图的邻接表
3拓扑排序
4.有向网的基本操作及应用
1创建有向网的邻接矩阵
2创建有向网的邻接表
3关键路径
4单源最短路径
5每对顶点之间的最短路径
实习报告
前言
图是一种比线性表和树更为复杂的数据结构。
在图形结构中,结点之间的关系可以是任意的,图中任意两个数据元素之间都有可能相关。
因此图的应用极为广泛,特别是近年来的迅速发展,已渗入诸如语言学、逻辑学、物理、化学、电讯工程、计算机科学以及数学的其他分支中。
要学好图这一章并熟练运用对我来说非常的困难,在这之前,必须学好《离散数学》中的图的理论,熟练掌握线性表的基本操作和应用,以及栈和队列还有树的相关知识。
限于这几章学得并不好,理解的也不透彻,所以在做课程设计时困难重重。
尽管遇到很多问题,但在这10天(每天3个小时)的不懈努力下,终于完成了。
为此,我简要介绍一下我所使用的工具和资料。
编译器:
vc6.0;
资料:
《数据结构(C语言版)》(严蔚敏吴伟民编著),互联网。
目录
绪论程序需求分析
第一部分程序功能运行示例展示
一、无向图的基本操作及应用
1.3无向图的深度优先遍历
1.4无向图的广度优先遍历
2、无向网的基本操作及应用
2.3普里姆(Prim)算法求最小生成树
三、有向图的基本操作及应用
3.3拓扑排序
4、有向图的基本操作及应用
4.3关键路径
4.4单源点最短路径
第二部分程序主要框架设计
第三部分程序主要算法分析(只包含下面几个算法)
一、普里姆(Prim)算法求最小生成树
二、拓扑排序
三、关键路径
四、单源点最短路径
第四部分程序不足及课设心得
第五部分程序代码附上
绪论—程序需求分析基础
按照要求,需要设计四种图,两种数据存储结构,十六中基本操作及应用,三层以上的显示菜单。
图的操作中又包含有有关线性表、栈和队列的基本操作。
由于显示菜单已给出,剩下的只是把函数写入其中,而线性表、栈和队列的基本操作并不复杂,很容易实现,我们只有完成图的相关操作即可。
图的操作都是以两种存储结构为基础的,邻接矩阵存储结构和邻接表存储结构,如四种图(有向图,有向网,无向图,无向网)的创建,其他的操作都是在四种图创建后才开始进行的。
所以,首先必须理解两种存储结构的定义。
图的邻接矩阵存储结构即图的数组表示法。
用两个数组分别存储数据元素(顶点)的信息和数据元素之间的关系(边或弧)的信息。
其优点是以二维数组表示有n个顶点的图时,需存放n顶点的信息和n*n个弧的信息存储量。
借助于邻接矩阵容易判定任意两个顶点之间是否有边或弧相连,并容易求各个顶点的度。
缺点其时间复杂度是O(n*n),例如,构造一个具有n个顶点和e条边的无向网的时间复杂度为O(n*n+e*n)。
图的邻接表存储结构是图的一种链式存储结构。
对图中的每个顶点建立一个单链表,每个结点有三个域组成,邻接点域adjvex(弧尾在邻接表链表中的位序),链域nextarc(下一条弧),数据域info(权值)。
还要建立一个邻接表链表,用以存放顶点名data和后继指针firstarc,表头结点以顺序结构的形式存储,便于随机访问任一顶点的单链表。
邻接表存储结构的优点在于其时间复杂度小。
深度优先遍历基本思想:
假设初始状态是图中所有顶点未曾被访问,则深度优先遍历可以从图中某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接到出发深度优先遍历图,直到图中所有和v有路径相通的顶点都被访问到;
若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为起始点,重复上述过程,直到图中所有顶点都被访问到为止。
广度优先遍历基本思想:
假设初始状态是图中所有顶点未曾被访问,则广度优先遍历可以从图中某个顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直到图中所有已被访问的顶点的邻接点都被访问到;
求无向网的最小生成树问题有两种算法:
Prima算法和Kruskal算法。
Prima算法是从网中的某个顶点出发,选择与该顶点有路径的所有顶点中路径最小的一条路径,从该最小路径的另一顶点出发,重复上述过程,直至图中所有顶点都被访问完成。
Kruskal算法是从网中找出所有路径最小的一条路径,记下已访问该路径的两个顶点,再次从图中找出剩下路径中的最小路径,重复上述过程,直至所有顶点都被访问完成,按访问的顺序依次输出各顶点,即构造了一棵最小生成树。
由某个集合上的一个偏序得到该集合的一个全序的操作就叫做拓扑排序。
如何进行拓扑排序呢?
(1)在有向图中选择一个没有前驱的顶点并输出;
(2)在图中删除该顶点和所有以它为尾的弧。
重复上述两步,直至全部顶点均已输出,就得到了一个拓扑有序序列。
求关键路径的算法如下(教科书184页):
(1)输入e条弧,建立AOE网的存储结构;
(2)从源点v0出发,令ve[0]=0,按拓扑有序求其余各顶点的最早发生时间。
如果得到的拓扑有序序列中的顶点个数小于网中的顶点数,则说明网中存在环,不能求关键路径,算法终止;
否则执行第三步。
(3)从汇点vn出发,令vl[n-1]=ve[n-1],按逆拓扑有序求其余各顶点的最迟发生时间vl[i];
(4)根据各顶点的和值,求每条弧的最早开始时间e(s)和最迟开始时间e(s),若某条弧满足条件e(s)=l(s),则为关键活动。
从某个源点到其余各顶点的最短路径问题:
若从v到vi有弧,则D[i]为弧上的权值,否则D[i]为无穷大。
显然,长度为D[j]=Min{D[i]|vi属于V}的路径就是从v出发的长度最短的一条路径。
单源最短路径问题--迪杰斯特拉(Dijkstra)算法
按路径长度递增的次序产生最短路径算法
把V分成两组:
S:
已求出最短路径的顶点的集合
T=V-S:
尚未确定最短路径的顶点集合
将T中顶点按最短路径长度递增的次序加入到S中,保证:
从源点V0到S中各顶点的最短路径长度都不大于从V0到T中任何顶点的最短路径长度
每个顶点对应一个距离值
S中顶点:
从V0到此顶点的最短路径长度
T中顶点:
从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度
计算关键路径的几个相关的量:
事件Vi的最早发生时间Ve(i)
从源点V1到顶点Vi的最长路径长度
事件Vi的最迟发生时间Vl[i]
在保证汇点Vn在Ve[n]时刻发生的前提下,事件Vi的允许的最晚时间。
等于Ve[n]减去vi到vn的最长路径长度。
活动ai的最早开始时间e[i]
设活动ai在边<
Vj,Vk>
上,则e[i]=Ve[j]。
活动ai的最迟开始时间l[i]
假定活动ai是用边<
vj,vk>
表示的,则ai的最迟开始时间l(i)=vl(k)-dut(<
j,k>
)。
求每一对顶点之间的最短路径:
解决这个问题的一个办法是:
每次以一个顶点为源点,重复执行迪杰斯特拉算法。
这样,便可求得每一对顶点之间的最短路径。
总的执行时间为O(n3)。
下面我们介绍另一种方法:
弗洛伊德算法
第一部分
——程序功能运行示例展示
1.3无向图的深度优先遍历
实例用图如下:
运行最终结果(具体算法请参见算法设计部分):
1.4无向图的广度优先遍历
运行最终结果:
二、无向网的基本操作及应用
2.3普里姆(Prim)算法求最小生成树
3.3拓扑排序
运行的最终结果:
四、有向网的基本操作及应用
4.3关键路径
4.4单源点最短路径
第二部分
——程序主要框架设计
把所有代码都放在同一个源文件中显然是不明智的;
因此,必须把各个算法的实现代码放在不同的头文件中,用#include“”包含其中,但这有先后顺序,首先应该包含的是含有状态结果的代码和库文件,其次是栈和队列的基本操作和实现,再就是图的两种存储结构的定义,四种图的创建和显示,然后是基于两种存储结构的各种算法的现,最后应该是三层显示输出菜单。
有向图的数据结的定义以及有关操作
主函数(基本菜单项框架的设计)
无向图的数据结的定义以及向关操作
队列数据结构的定义以及向关操作
无向网的数据结的定义以及向关操作
栈的数据结的定义以及有关操作
图的数据结的定义
有向网的数据结的定义以及有关操作
第三部分
——程序主要算法分析
1、普里姆(Prim)算法求最小生成树
普里姆(Prim)算法
基本思想
假设N=(V,E)是一个具有n个顶点的连通网,T=(U,TE)是所求的最小生成树,其中U是T的顶点集,TE是T的边集。
算法从U={u0}(u0∈V),TE={}开始
重复执行以下操作:
在所有u∈V,v∈V-U的边(u,v)∈E中找一条代价最小的边(u0,v0)并入集合TE,同时v0并入U,直到U=V为止。
此时TE中必有n-1条边,则T为N的最小生成树。
//Prim算法求最小生成树
voidprim(MGraph&
G)
/*每一步扫描数组lowcost,在V-U中找出离U最近的顶点,令其为k,
并打印边(k,closest[k]),然后修改lowcost和closest*/
{
inti,j,k,min;
intlowcost[MAXVEX];
//存放U顶点中顶点到V-U集合中各顶点的各边上的当前最小权值
intclosest[MAXVEX];
//将其lowcost改为0,则表示它已加入生成树顶点集合
for(i=2;
i<
=G.vexnum;
i++)/*从顶点2开始*/
{
lowcost[i]=G.arcs[1][i];
//把弧的权值给数组lowcost
closest[i]=1;
}//选择从顶点出发,数组的初始状态
closest[1]=0;
//把顶点1加入最小生成树中
cout<
<
"
最小代价生成树的各条边为\n"
;
i++)/*从u之外求离u中某一顶点最近的顶点*/
min=MAXCOST;
j=1;
//从邻接矩阵的第一行开始遍历,求出离头顶点最近的顶点
k=i;
while(j<
=G.vexnum)
{
if(lowcost[j]<
min&
&
closest[j]!
=0)
min=lowcost[j];
//遇到最小权值的边存储在min中
k=j;
}
elsej++;
}
printf("
(%d,%d)"
closest[k],k);
//打印边权值最小的边
closest[k]=0;
/*k加入如到最小生成树中*/
for(j=2;
j<
j++)//然后再用刚加入的顶点与其他不为0的顶点依次比较更新权值
if(closest[j]!
=0&
G.arcs[k][j]<
lowcost[j])
{
lowcost[j]=G.arcs[k][j];
closest[j]=k;
}
}
2、拓扑排序
拓扑排序
AOV网:
在有向图中,若用顶点表示活动,弧表示活动之间的先后关系,这种有向图称为顶点表示活动的网简称AOV网。
在AOV网中,若从顶点vi到顶点vj有一条有向路径,则vi是vj的前驱,vj是vi的后继。
若<
vi,vj>
是网中的一条弧,则vi是vj的直接前驱,vj是vi的直接后继。
如果vi是vj的前驱或直接前驱,则活动vi必须在vj开始之前结束,活动vj必须在活动vi结束之后才能开始。
//求关键路径
intve[MAXVEX],v1[MAXVEX];
intindegree[MAXVEX];
intTopoOder(ALGraphG,SqStack&
T)
{
inti,j,k,count=0;
ArcNode*p;
SqStackS;
InitStack(S);
FindInDegree(G,indegree);
for(i=1;
i++)//初始化所有点的最早发生时间为零
{ve[i]=0;
}
//求各顶点入度
i++)//循环执行直至遍历所有点
if(indegree[i]==0)
Push(S,i);
while(S.top!
=S.base)
{
Pop(S,j);
Push(T,j);
//j号顶点入栈并计数
count++;
for(p=G.vertices[j].firstarc;
p;
p=p->
next)
{
k=p->
adjvex;
indegree[k]=indegree[k]-1;
if(indegree[k]==0)
Push(S,k);
if(ve[j]+*(p->
info)>
ve[k])//排序过程中求顶点的ve[i]
ve[k]=ve[j]+*(p->
info);
}
if(count<
G.vexnum)
return0;
//有向网有回路
elsereturn1;
intCriticalPath(ALGraphG)//关键路径
{intvl[MAXVEX];
ArcNode*p;
inti,j,k,ee,el,dut;
SqStackT;
InitStack(T);
if(!
TopoOder(G,T))//按拓扑逆序求各顶点的vl值
return0;
for(i=1;
i++)
vl[G.vexnum-i+1]=ve[G.vexnum];
//初始化
while(T.top!
=T.base)
for(Pop(T,j),p=G.vertices[j].firstarc;
k=p->
dut=*(p->
if(vl[k]-dut<
vl[j])
vl[j]=vl[k]-dut;
//事件最小的最晚发生时间
cout<
起点终点权值最早发生时间最迟发生时间"
endl;
for(j=1;
j++)//求ee,el,关键活动
for(p=G.vertices[j].firstarc;
ee=ve[j];
//活动的最早发生时间是前一个事件的最早发生事件
el=vl[k]-dut;
//活动的最晚发生时间是后一个事件减去路上要用的时间
if(ee==el)
cout<
setw(4)<
G.vertices[j].data<
G.vertices[k].data<
dut<
setw(8)<
ee<
setw(12)<
el<
三、关键路径
求关键路径的算法如下:
每一次以一个顶点为源点,重复执行从某个源点到其余各顶点的最短路径的算法n次。
这样,就可以求得每一对顶点之间的最短路径。
四、单源点最短路径
Dijkstra提出了一个按路径“长度”递增的次序,逐步得到由给定源点到图的其余各点间的最短路径的算法:
假设我们以邻接矩阵cost表示所研究的有向网。
迪杰斯特拉算法需要一个顶点集合,初始时集合内只有一个源点V0,以后陆续将已求得最短路径的顶点加入到集合中,到全部顶点都进入集合了,过程就结束了。
集合可用一维数组来表示,设此数组为S,凡在集合S以外的顶点,其相应的数组元素S[i]为0,否则为1。
另需一个一维数组D,每个顶点对应数组的一个单元,记录从源点到其他各顶点当前的最短路径长度,其初值为D[i]=cost[V0][i],i=1…n。
数组D中的数据随着算法的逐步进行要不断地修改
定义了S集合和D数组并对其初始化后,迪杰斯特拉算法在进行中,都是从S之外的顶点集合中选出一个顶点w,使D[w]的值最小。
于是从源点到达w只通过S中的顶点,把w加入集合S中,并调整D中记录的从源点到集合中每个顶点v的距离:
取D[v]和D[w]+cost[w][v]中值较小的作为新的D[v]
重复上述,直到S中包含V中其余各顶点的最短路径。
//求单源顶点最短路径
//为实现算法,引入一个辅助数组dist[n],
//含两个成员length和pre,该数组的第j个元素dist[j],
//用来保存从源点i到终点j的目前最短路径长度及该路经上的前驱结点。
typedefintPathMatrix[MAXVEX][MAXVEX];
//路径矩阵