图的最短路径、拓扑排序和关键路径
- 格式:doc
- 大小:197.50 KB
- 文档页数:23
数据结构的应用的拓扑排序与关键路径算法拓扑排序与关键路径算法是数据结构中重要的应用之一。
拓扑排序通过对有向图的节点进行排序,使得对于任意一条有向边(u,v),节点 u 在排序中都出现在节点 v 之前。
关键路径算法则是用来确定一个项目的关键活动和最短完成时间。
拓扑排序的实现可以通过深度优先搜索或者广度优先搜索来完成。
深度优先搜索是递归地访问节点的所有未访问过的邻居节点,直到没有未访问过的邻居节点为止,然后将该节点添加到拓扑排序的结果中。
广度优先搜索则是通过使用队列来实现的,将节点的邻居节点逐个入队并进行访问,直到队列为空为止。
无论使用哪种方法,拓扑排序都可以通过判断节点的入度来进行。
拓扑排序在很多实际问题中都有广泛应用。
比如在任务调度中,拓扑排序可以用来确定任务间的依赖关系和执行顺序;在编译原理中,拓扑排序可以用来确定程序中变量的定义和使用顺序。
关键路径算法用于确定项目中的关键活动和最短完成时间。
它通过计算每个活动的最早开始时间和最晚开始时间,以及每个活动的最早完成时间和最晚完成时间来实现。
具体步骤如下:1. 构建有向加权图,其中节点表示项目的活动,有向边表示活动间的先后关系,边的权重表示活动的持续时间。
2. 进行拓扑排序,确定活动的执行顺序。
3. 计算每个活动的最早开始时间,即从起始节点到该节点的最长路径。
4. 计算每个活动的最晚开始时间,即从终止节点到该节点的最长路径。
5. 根据每个活动的最早开始时间和最晚开始时间,可以确定关键活动,即最早开始时间与最晚开始时间相等的活动。
6. 计算整个项目的最短完成时间,即从起始节点到终止节点的最长路径。
拓扑排序与关键路径算法在工程管理、任务调度、生产流程优化等领域都有重要应用。
它们能够帮助我们有效地组织和管理复杂的项目,提高工作效率和资源利用率。
在实际应用中,我们可以借助计算机编程以及各种图算法库来实现这些算法,从而更快速、准确地解决实际问题。
综上所述,拓扑排序与关键路径算法是数据结构的重要应用之一。
数据结构图练习题数据结构图练习题在计算机科学领域中,数据结构是一种用来组织和存储数据的方式。
而图是一种常见的数据结构,它由一组节点和连接这些节点的边组成。
图可以用来表示各种各样的关系,比如社交网络中的用户关系、城市之间的道路网络等等。
在本文中,我们将探讨一些与图相关的练习题,帮助读者更好地理解和应用图的概念。
1. 最短路径问题最短路径问题是图论中的经典问题之一。
给定一个带权重的有向图,我们需要找到从一个起始节点到目标节点的最短路径。
这里的权重可以表示为距离、时间或者其他度量。
解决这个问题的算法有很多,其中最著名的是Dijkstra算法和Bellman-Ford算法。
读者可以尝试使用这些算法来解决一些具体的实例,比如计算两个城市之间的最短路径。
2. 拓扑排序拓扑排序是对有向无环图(Directed Acyclic Graph,简称DAG)进行排序的一种算法。
在一个DAG中,节点之间存在一种偏序关系,即某些节点必须在其他节点之前进行处理。
拓扑排序可以帮助我们确定这种偏序关系,从而找到一种合理的处理顺序。
比如,在编译器中,拓扑排序可以用来确定源代码中各个函数的调用顺序。
读者可以尝试编写一个拓扑排序算法,并应用到一些具体的场景中。
3. 最小生成树最小生成树是一个无向连通图中一棵权值最小的生成树。
在一个连通图中,最小生成树可以帮助我们找到一种最优的连接方式,以满足一些约束条件。
最常用的算法是Prim算法和Kruskal算法。
读者可以尝试使用这些算法来解决一些具体的实例,比如在一个城市之间建设光纤网络,以最小的成本实现全覆盖。
4. 图的遍历图的遍历是指按照某种方式访问图中的所有节点。
常见的遍历算法有深度优先搜索(DFS)和广度优先搜索(BFS)。
DFS通过递归地访问每个节点的邻居节点,直到所有节点都被访问完。
BFS则通过队列来实现,先访问起始节点的邻居节点,然后依次访问它们的邻居节点,直到所有节点都被访问完。
3、栈和队列的应用
五、串
1、串的定义
2、串的存储结构及基本操作
3、串的应用
六、数组和广义表
1、数组和广义表的定义
2、数组和广义表的存储结构及基本操作
3、矩阵的压缩存储
4、数组和广义表的应用
七、树和二叉树
1、树的定义和基本操作
2、二叉树的定义、性质和存储结构及基本操作
3、遍历二叉树和线索二叉树
4、树和森林(存储结构、遍历、与二叉树的互相转换)
5、哈夫曼树及其应用
八、图
1、图的定义
2、图的存储结构
3、图的遍历
4、图的连通性(连通分量、最小生成树)
5、图的拓扑排序、关键路径、最短路径
九、查找
1、顺序表、有序表的查找及其分析
2、二叉排序树和平衡二叉树、B树
3、散列(Hash)表的定义、Hash函数的构造方式、冲突处理和Hash表的查找及其分析
十、内部排序
1、内部排序的基本概念
2、各种(插入类、交换类、选择类、归并类、基数排序)内部排序方法及其分析比较
3、外部排序的基本概念与方法
十一、文件
1、有关文件的基本概念
2、顺序文件、索引文件、索引顺序文件、直接存取文件、多重链表文件、倒排文件等的基本存取方法。
数据结构课程辅导---图的最短路径、拓扑排序和关键路径一、最短路径由图的概念可知,在一个图中,若从一顶点到另一顶点存在着一条路径(这里只讨论无回路的简单路径),则称该路径长度为该路径上所经过的边的数目,它也等于该路径上的顶点数减1。
由于从一顶点到另一顶点可能存在着多条路径,每条路径上所经过的边数可能不同,即路径长度不同,我们把路径长度最短(即经过的边数最少)的那条路径叫做最短路径,其路径长度叫做最短路径长度或最短距离。
上面所述的图的最短路径问题只是对无权图而言的,若图是带权图,则把从一个顶点i到图中其余任一个顶点j的一条路径上所经过边的权值之和定义为该路径的带权路径长度,从vi到vj可能不止一条路径,我们把带权路径长度最短(即其值最小)的那条路径也称作最短路径,其权值也称作最短路径长度或最短距离。
例如,在图3-1中,从v0到v4共有三条路径:{0,4},{0,1,3,4}和{0,1,2,4},其带权路径长度分别为30,23和38,可知最短路径为{0,1,3,4},最短距离为23。
图3-1 带权图和对应的邻接矩阵实际上,这两类最短路径问题可合并为一类,这只要把无权图上的每条边标上数值为1的权就归属于有权图了,所以在以后的讨论中,若不特别指明,均认为是求带权图的最短路径问题。
求图的最短路径问题用途很广。
例如,若用一个图表示城市之间的运输网,图的顶点代表城市,图上的边表示两端点对应城市之间存在着运输线,边上的权表示该运输线上的运输时间或单位重量的运费,考虑到两城市间的海拔高度不同,流水方向不同等因素,将造成来回运输时间或运费的不同,所以这种图通常是一个有向图。
如何能够使从一城市到另一城市的运输时间最短或者运费最省呢?这就是一个求两城市间的最短路径问题。
求图的最短路径问题包括两个方面:一是求图中一顶点到其余各顶点的最短路径,二是求图中每对顶点之间的最短路径。
下面分别进行讨论。
1. 从一顶点到其余各顶点的最短路径对于一个具有n个顶点和e条边的图G,从某一顶点vi(称此为源点)到其余任一顶点vj(称此为终点)的最短路径,可能是它们之间的边(i,j)或<i,j>,也可能是经过k个(1≤k≤n-2,最多经过除源点和终点之外的所有顶点)中间顶点和k+1条边所形成的路径。
例如在图3-1中,从v0到v1的最短路径就是它们之间的有向边<0,1>,其长度为3;从v0到v4的最短路径经过两个中间点v1和v3以及三条有向边<0,1>,<1,3>和<3,4>,其长度为23。
那么,如何求出从源点i到图中其余每一个顶点的最短路径呢?狄克斯特拉(Dijkstra)于1959年提出了解决此问题的一般算法,具体做法是按照从源点到其余每一顶点的最短路径长度的升序依次求出从源点到各顶点的最短路径及长度,每次求出从源点i到一个终点m的最短路径及长度后,都要以该顶点m作为新考虑的中间点,用vi到vm的最短路径和最短路径长度对vi到其它尚未求出最短路径的那些终点的当前最短路径及长度作必要地修改,使之成为当前新的最短路径和最短路径长度,当进行n-2次(因最多考虑n-2个中间点)后算法结束。
狄克斯特拉算法需要设置一个集合,假定用S表示,其作用是保存已 2求得最短路径的终点序号,它的初值中只有一个元素,即源点i,以后每求出一个从源点i到终点m的最短路径,就将该顶点m并入S集合中,以便作为新考虑的中间点;还需要设置一个整型(假定权值类型为整型)数组dist[MaxVertexNum],该数组中的第j个元素dist[j]用来保存从源点i到终点j的目前最短路径长度,它的初值为(i,j)或<i,j>边上的权值,若vi到vj没有边,则权值为MaxValue,以后每考虑一个新的中间点时,dist[j]的值可能变小;另外,再设置一个与dist数组相对应的、类型为adjlist的邻接表表头向量数组path,该数组中的第j个元素path[j]指向一个单链表,该单链表中保存着从源点i到终点j的目前最短路径,即一个顶点序列,当vi到vj存在着一条边时,则path[j]初始指向由顶点i和j构成的单链表,否则path[j]的初值为空。
此算法的执行过程是:首先从S集合以外的顶点(即待求出最短路径的终点)所对应的dist数组元素中,查找出其值最小的元素,假定为dist[m],该元素值就是从源点i到终点m的最短路径长度(证明从略),对应path数组中的元素path[m]所指向的单链表链接着从源点i到终点m的最短路径,即经过的顶点序列或称边序列;接着把已求得最短路径的终点m并入集合S中;然后以vm作为新考虑的中间点,对S集合以外的每个顶点j,比较dist[m]+GA[m][j](GA为图G的邻接矩阵)与dist[j]的大小,若前者小于后者,表明加入了新的中间点vm之后,从vi到vj的路径长度比原来变短,应用它替换dist[j]的原值,使dist[j]始终保持到目前为止最短的路径长度,同时把path[m]单链表复制到path[j]上,并在其后插入vj结点,使之构成从源点i到终点j的目前最短路径。
重复n-2次上述运算过程,即可在dist数组中得到从源点i到其余每个顶点的最短路径长度,在path数组中得到相应的最短路径。
为了简便起见,可采用一维数组s[n]来保存已求得最短路径的终点的集合S,具体做法是:若顶点j在集合S中,则令数组元素s[j]的值取1,否则取0。
这样,当判断一个顶点j是否在集合S以外时,只要判断对应的数组元素s[j]是否为0即可。
例如,对于图3-1来说,若求从源点v0到其余各顶点的最短路径,则开始时三个一维数组s,dist和path的值为:0 1 2 3 4 s dist path下面开始进行第一次运算,求出从源点v0到第一个终点的最短路径。
首先从s元素为0的对应dist元素中,查找出值最小的元素,求得dist[2]的值最小,所以第一个终点为v1, 最短距离为dist[2]=3,最短路径为path[2]={0,1},接着把s[1]置为1,表示v1已加入S集合中,然后以v1为新考虑的中间点,对s数组中元素为0的每个顶点j(此时2≤j≤4)的目前最短路径长度dist[j]和目前最短路径path[j]进行必要地修改,因dist[1]+GA[1][2]=3+25=28,小于dist[2]=∞,所以将28赋给dist[2],将path[1]并上v2后赋给path[2],同理因dist[1]+GA[1][3]=3+8=11,小于dist[3]=∞,所以将11赋给dist[3],将path[1]并上v3后赋给path[3],最后再看从v0到v4,以v1作为新考虑的中间点的情况,由于v1到v4没有出边,所以GA[1][4]=∞,故dist[1]+GA[1][4]不小于dist[4],因此dist[4]和path[4]无需修改,应维持原值。
至此,第一次运算结束,三个一维数组的当前状态为:0 1 2 3 4 s dist path下面开始进行第二次运算,求出从源点v0到第二个终点的最短路径。
首先从s数组中元素为0的对应dist元素中,查找出值最小的元素,求得dist[3]的值最小,所以第二个终点为v3, 最短距离为dist[3]=11,最短路径为path[3]={0,1,3},接着把s[3]置为1,然后以v3作为新考虑的中间点,对s中元素为0的每个顶点j(此时j=3,5)的dist[j]和path[j]进行必要的修改,因dist[3]+GA[3][2]=11+4=15,小于dist[2]=28,所以将15赋给dist[2],将path[3]并上v2后赋给path[2],同理,因dist[3]+GA[3][4]=11+12=23,小于dist[4]=30,所以将23赋给dist[4],将path[3]并上v4后赋给path[4]。
至此,第二次运算结束,三个一维数组的当前状态为:0 1 2 3 4 s dist path下面开始进行第三次运算,求出从源点v0到第三个终点的最短路径。
首先从s中元素为0的对应dist元素中,查找出值最小的元素为dist[2],所以求得第三个终点为v2,最短距离为dist[2]=15,最短路径为path[2]={0,1,3,2},接着把s[2]置为1,然后以v2作为新考虑的中间点,对s中元素为0的每个顶点j(此时只有v4一个)的dist[j]和path[j]进行必要的修改,因dist[2]+GA[2][4]=15+10=25,大于dist[4]=23,所以无需修改,原值不变。
至此,第三次运算结束,三个一维数组的当前状态为:0 1 2 3 4 s dist path由于图中有5个顶点,只需运算3次,即n-2次,虽然此时还有一个顶点未加入S集合中,但它的最短路径及最短距离已经最后确定,所以整个运算结束。
最后在dist中得到从源v0到每个顶点的最短路径长度,在path中得到相应的最短路径。
如果用图形表示上述过程中每次运算的结果,则对应的图形分别如图3-2(b)~(e)所示,其中实线有向边所指向的顶点为集合S中的顶点,虚线有向边所指向的顶点为集合S外的顶点;S集合中的顶点上所标数值为从源点v0到该顶点的最短路径长度,从源点v0到该顶点所经过的有向边为从v0到该顶点的最短路径;S集合外的顶点上所标数值为从源点v0到该顶点的目前最短路径长度,从v0到该顶点所经过的有向边为从v0到该顶点的目前最短路径。
为了便于对照分析,把图3-1(a)重画于图3-2(a)中。
图3-2 利用狄克斯特拉算法求最短路径的图形说明根据以上分析和举例,不难给出狄克斯特拉算法的描述: void Dijkstra(adjmatrix GA, int dist[],adjlist path, int i, int n)//利用狄克斯特拉算法求图GA中从顶点i到其余每个顶点间的 //最短距离和最短路径,它们分别被存于数组dist和path数组中{int j,k,w,m;//定义作为集合使用的动态数组sint* s=new int[n];//分别给s,dist和path数组赋初值for(j=0; j<n; j++) {if(j==i)s[j]=1;elses[j]=0;dist[j]=GA[i][j];if(dist[j]<MaxValue && j!=i) {edgenode* p1=new edgenode;edgenode* p2=new edgenode;p1->adjvex=i;p2->adjvex=j;p2->next=NULL;p1->next=p2;path[j]=p1;}elsepath[j]=NULL;}//共进行n-2次循环,每次求出从源点i到终点m的最短路径及长度 for(k=1;k<=n-2; k++){//求出第k个终点mw=MaxValue; m=i;for(j=0; j<n; j++)if(s[j]==0 && dist[j]<w) {w=dist[j];m=j;}//若条件成立,则把顶点m并入集合S中,否则退出循环,因为剩余 //的顶点,其最短路径长度均为MaxValue,无需再计算下去 if(m!=i)s[m]=1;elsebreak;//对s元素为0的对应dist和path中的元素作必要修改 for(j=0; j<n; j++)if(s[j]==0 && dist[m]+GA[m][j]<dist[j]) { dist[j]=dist[m]+GA[m][j];PATH(path, m, j); //调用此函数,由到顶点m的 //最短路径和顶点j构成到顶点j 的目前最短路径 }}}PATH函数的定义如下:void PATH(adjlist path, int m, int j)//由到顶点m的最短路径和顶点j构成到顶点j的目前最短路径 {edgenode *p, *q, *s;//把顶点j的当前最短路径清除掉p=path[j];while(p!=NULL) {path[j]=p->next;delete p;p=path[j];}//把到顶点m的最短路径拷贝过来到顶点j的最短路径上 p=path[m];while(p!=NULL) {q=new edgenode;q->adjvex=p->adjvex;if(path[j]==NULL)path[j]=q;elses->next=q;s=q;p=p->next;}//把顶点j加入到path[j]单链表的最后,形成新的目前最短路径 q=new edgenode; q->adjvex=j;q->next=NULL;s->next=q;}2. 每对顶点之间的最短路径求图中每对顶点之间的最短路径是指把图中任意两个顶点vi和vj(i≠j)之间的最短路径都计算出来。