Presentation is loading. Please wait.

Presentation is loading. Please wait.

DFS和BFS用来干什么? 连通性 拓扑排序 关键路径.

Similar presentations


Presentation on theme: "DFS和BFS用来干什么? 连通性 拓扑排序 关键路径."— Presentation transcript:

1 DFS和BFS用来干什么? 连通性 拓扑排序 关键路径

2 图的连通性问题 连通分量 (Connected component)
当无向图为非连通图时, 从图中某一顶点出发, 利用DFS或BFS不可能遍历到图中的所有顶点, 只能访问到该顶点所在的极大连通子图(连通分量)的所有顶点。 若从无向图的每一个连通分量中的一个顶点出发进行遍历, 可求得无向图的所有连通分量。

3 求连通分量的算法需要对图的每一个顶点进行检测:若已被访问过,则该顶点一定是落在图中已求得的连通分量上;若还未被访问,则从该顶点出发遍历图,可求得图的另一个连通分量。
对于非连通的无向图,所有连通分量的生成树组成了非连通图的生成森林。

4 A H K B C D E I L M N F G J O 非连通无向图 A H K B C D E I L M N F G J O 非连通图的连通分量

5 最小生成树 ( Minimum Cost Spanning Tree )
使用不同的遍历图的方法,可以得到不同的生成树;从不同的顶点出发,也可能得到不同的生成树。 按照生成树的定义,n 个顶点的连通网络的生成树有 n 个顶点、n-1 条边。 构造最小生成树的准则 必须使用且仅使用该网络中的n-1 条边来联结网络中的 n 个顶点; 不能使用产生回路的边; 各边上的权值的总和达到最小。

6 MST 性质 假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值(代价)的边,其中uU,v V-U,则必存在一棵包含边(u,v)的最小生成树。 V- U U u v u’ v’

7 普里姆(Prim)算法 普里姆算法的基本思想:
从连通网络 N = ( V, {E} )中的某一顶点 u0 出发, U={u0}。选择与u0关联的具有最小权值的边 ( u0, v0 ), 将其顶点v0加入到生成树顶点集合U中。 以后每一步从一个顶点在 U 中,而另一个顶点不在 U 中的各条边中选择权值最小的边(u, v), 把顶点加入v到集合 U 中。如此继续下去, 直到网络中的所有顶点都加入到生成树顶点集合 U 中为止。 采用邻接矩阵作为图的存储表示。

8 5 4 6 1 3 2 28 10 25 14 24 22 16 18 12 5 10 4 6 1 3 2 5 4 6 1 3 2 10 25 原图 (a) (b) 25 5 4 6 1 3 2 10 22 25 5 4 6 1 3 2 10 12 5 4 6 1 2 10 25 14 16 12 3 22 22 (c) (d) (e) (f)

9 在构造过程中,设置一个辅助数组: closedge[0..vexnum-1 ]: 记录从U到V-U具有最小代价的边; 对每个顶点vi V-U : closedge[ i-1 ].lowcost=Min{cost(u, vi)|u U}, closedge[i-1].adjvex: 对应这条边依附的U中的顶点; 例子 28 1 10 14 16 5 6 2 24 18 12 25 4 3 22

10 Prim 算法 void MiniSpanTree_PRIM ( MGraph G, VertexType u ) {
struct{ VertexType adjvex; VRType lowcost; }closedge[MAX_VERTEX_NUM]; k=LocateVex(G,u); for (j=0;j<G.vexnum;++j) if (j !=k) closedge[j]={u,G.arcs[k][j].adj}; closedge[k].lowcost =0; //初始,U={u} for (i=1;i<G.vexnum;++){ //选择其余G.vexnum-1个顶点 k=minimum(closedge); //求出T的下个顶点:第k顶点 printf(closedge[k].adjvex,G.vexs[k]); closedge[k].lowcost = 0; //第k顶点并入U集 for ( j=0;j<G.vexnum;++j) //调整 if (G.arcs[k][j].adj < closedge[j].lowcost) closedge [j] = {G.vex[k],G.arcs[k][j].adj}; } 算法 7.9

11 分析以上算法, 设连通网络有 n 个顶点, 则该算法的时间复杂度为 O(n2), 它适用于稠密网。
注意:当各边有相同权值时,由于选择的随意性,产生的生成树可能不唯一。当各边的权值不相同时,产生的生成树是唯一的。

12 克鲁斯卡尔(Kruskal)算法 算法思想:从n个孤立顶点出发,顺次加入n-1条边。
假设连通网N=(V,{E}),令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。 在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入,否则舍去该边而选择下一条代价最小的边。 依次类推,直至T中所有顶点都在同一连通分量上为止。 算法时间复杂度 O(eloge),适用于稀疏网。

13 有向无环图(DAG)及其应用 定义:一个无环的有向图称为有向无环图。
有向无环图是描述含有公共子式的表达式以及一项工程或系统进行过程的有效工具。 用顶点表示活动的网络 (AOV网络) 计划、施工过程、生产流程、程序流程等都是“工程”。除了很小的工程外,一般都把工程分为若干个叫做“活动”的子工程。完成了这些活动,这个工程就可以完成了。 例如,计算机专业学生的学习就是一个工程,每一门课程的学习就是整个工程的一些活动。其中有些课程要求先修课程,有些则不要求。这样在有的课程之间有领先关系,有的课程可以并行地学习。

14 课程代号 课程名称 先修课程 C1 高等数学 C2 程序设计基础 C3 离散数学 C1, C2 C4 数据结构 C3, C2
课程代号 课程名称 先修课程 C 高等数学 C 程序设计基础 C 离散数学 C1, C2 C 数据结构 C3, C2 C 高级语言程序设计 C2 C 编译方法 C5, C4 C 操作系统 C4, C9 C 普通物理 C1 C 计算机原理 C8

15 C8 C3 C5 C4 C9 C6 C7 C1 C2 学生课程学习工程图

16 可以用有向图表示一个工程。在这种有向图中,用顶点表示活动,用有向边<Vi, Vj>表示活动Vi 必须先于活动Vj 进行。这种有向图叫做顶点表示活动的AOV网络 (Activity On Vertices)。 在AOV网络中不能出现有向回路, 即有向环。如果出现了有向环,则意味着某项活动应以自己作为先决条件。 因此,对给定的AOV网络,必须先判断它是否存在有向环。

17 检测有向环的一种方法是对AOV网络构造它的拓扑有序序列。即将各个顶点 (代表各个活动)排列成一个线性有序的序列,使得AOV网络中所有应存在的前驱和后继关系都能得到满足。

18 如果AOV网络中存在有向环,此AOV网络所代表的工程是不可行的。
例如, 对学生选课工程图进行拓扑排序, 得到的拓扑有序序列为 C1 , C2 , C3 , C4 , C5 , C6 , C8 , C9 , C7 或 C1 , C8 , C9 , C2 , C5 , C3 , C4 , C7 , C6 C8 C3 C5 C4 C9 C6 C7 C1 C2

19 拓扑排序的方法 ① 输入AOV网络。令 n 为顶点个数。 ② 在AOV网络中选一个没有直接前驱的顶点, 并输出之;
③ 从图中删去该顶点, 同时删去所有它发出的有向边; ④ 重复以上 ②、③步, 直到 全部顶点均已输出,拓扑有序序列形成,拓扑排序完成;或 图中还有未输出的顶点, 但已跳出处理循环。说明图中还剩下一些顶点, 它们都有直接前驱。这时网络中必存在有向环。

20 拓扑排序的过程 (a) 有向无环图 (b) 输出顶点C4 (c) 输出顶点C0 (d) 输出顶点C3 C0 C1 C2 C3 C4 C5

21 (e) 输出顶点C2 (f) 输出顶点C1 (g) 输出顶点C5 (h) 拓扑排序完成
最后得到的拓扑有序序列为 C4 , C0 , C3 , C2 , C1 , C5 。它满足图中给出的所有前驱和后继关系,对于本来没有这种关系的顶点,如C4和C2,也排出了先后次序关系。(即,得到了顶点集的一个全序关系)

22 AOV网络及其邻接表表示 1 2 3 4 5 1 3 C0 C1 C2 C3 ^ C4 C5 ^ 1 3 0 5 1 5 0 1 5 0
dest link indegree data adj 1 2 3 4 5 1 3 C0 C1 C2 C3 ^ C4 C5 ^ 1 3 0 5 1 5 0 1 5 0 C0 C1 C2 C3 C4 C5

23 在邻接表中增设一个数组indegree[ ],记录各顶点入度。。入度为零的顶点即无前驱顶点。
在输入数据前, 顶点表VexList[ ]和入度数组indegree[ ]全部初始化。在输入数据时, 每输入一条边<j, k>, 就需要建立一个边结点, 并将它链入相应边链表中, 统计入度信息: EdgeNode * p = new EdgeNode; p->dest = k; //建立边结点 p->link = G.VexList[j].firstAdj; VexList[j].firstAdj = p; //链入顶点 j 的边链表的前端 indegree[k]++; //顶点 k 入度加一

24 在算法中, 使用一个存放入度为零的顶点的链式栈, 供选择和输出无前驱的顶点。
拓扑排序算法可描述如下: 建立入度为零的顶点栈; 当入度为零的顶点栈不空时, 重复执行 从顶点栈中退出一个顶点, 并输出之; 从AOV网络中删去这个顶点和它发出的边, 边的终顶点入度减一; 如果边的终顶点入度减至0, 则该顶点进入度为零的顶点栈; 如果输出顶点个数少于AOV网络的顶点个数, 则报告网络中存在有向环。

25 最短路径 (Shortest Path) 最短路径问题:如果从图中某一顶点(称为源点)到达另一顶点(称为终点)的路径可能不止一条,如何找到一条路径使得沿此路径上各边上的权值总和达到最小? 问题解法: 边上权值非负情形的单源最短路径问题 — Dijkstra算法 边上权值为任意值的单源最短路径问题 — Bellman和Ford算法 所有顶点之间的最短路径 — Floyd算法

26 边上权值非负情形的单源最短路径问题 问题的提法: 给定一个带权有向图D与源点 v,求从 v 到D中其它顶点的最短路径。限定各边上的权值大于或等于0。 为求得这些最短路径, Dijkstra提出按路径长度的递增次序, 逐步产生最短路径的算法。首先求出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径,依次类推,直到从顶点v到其它各顶点的最短路径全部求出为止。 举例说明

27 10 100 1 30 10 4 50 60 2 3 20 Dijkstra逐步求解的过程 源点 终点 最短路径 路径长度 v v (v0,v1) v (v0,v3,v2) v (v0,v3) v (v0,v3,v2 ,v4)

28 S:已求得的最短路径的终点集 V-S vk v vj … 调整: 每确定一个次短最短路径终点Vj , 1.将其并入S集;
2.调整V-S中其余顶点vk的“当前找到的从源点 v到终点 vk 的最短路径的长度D[k]”: D[k] ← Min{ D[k], D[j] + arcs[j][k] } S:已求得的最短路径的终点集 V-S vk v vj 下一条最短路径(设其终点为vj),或者是弧(v, vj),或者是从v出发,中间只经过 S 中的顶点而最后到达vj的路径。

29 引入辅助向量D。它的每一个分量D[i]表示当前找到的从源点 v到终点 vi 的最短路径的长度。初始状态:
若从源点v到顶点 vi 有弧, 则D[i]为该边上的权值; 若从源点v到顶点 vi 无弧, 则D[i]为 。 假设 S 是已求得的最短路径的终点的集合,则可证明:下一条最短路径(设其终点为x)或者是弧(v,x),或者是从v 出发,中间只经过 S 中的顶点而最后到达x的路径。 每次求得一条最短路径后, 其终点vj 加入集合S,然后对所有的vk V-S,修改其 D[i]值。

30 Dijkstra算法 ① 初始化: S ← { v}; D[i] ← arcs[LocateVex(G,v)][i] ② 选择vj, 使得:
D[j] ← Min { D[i] }, vi  V- S ; S ← S U { j }; ③ 修改: D[k] ← Min{ D[k], D[j] + arcs[j][k] }, 对于每一个 vk  V- S ; ④ 判断:若 S = V, 则算法结束,否则转 ②。

31 for (v=0;v<G.vexnum;++v){ //初始化final,D , P
void ShortestPath_DIJ (MGraph G, int v0,PathMatrix &p,ShortPathTable &D ){ //用Dijkstra算法求有向网G的v0顶点到其余顶点的最短路径P[v]及其带权 //长度D[v]。 //若P[v][w]为TRUE,则w是从v0到v当前求得最短路径长度上的顶点。 //final[v]为TRUE当且仅当vS,即已经求得从v0到v的最短路径。 for (v=0;v<G.vexnum;++v){ //初始化final,D , P final[v]=FALSE;D[v]=G.arcs[v0][v]; for (w=0;w<G.vexnum;++w) P[v][w]=FALSE; if (D[v]<INFINITY) //<v0,v>存在,v0和v是当前求得最短路径上的顶点 {P[v][v0]=TRUE;P[v][v]=TRUE;} } D[v0]=0;final[v0]=TRUE; //S ← { v0};

32 for (i=1;i<G.vexnum;++i){ //其余G.vexnum-1个顶点
//开始主循环每次求得v0到某个v顶点的最短路径,并加v到S集。 for (i=1;i<G.vexnum;++i){ //其余G.vexnum-1个顶点 min=INFINITY; //当前所知离v0顶点最近距离 for (w=0;w<G.vexnum;++w) if (! final[w]) //w  V- S if (D[w]<min) {v=w;min=D[w];} final[v]=TRUE; //v并入S集 for (w=0;w<G.vexnum;++w) //更新当前最短路径及距离 if ( !final[w] && (min +G.arcs[v][w]< D[w])){ D[w]=min + G.arcs[v][w]; P[w]=P[v];P[w][w]=TRUE; // P[w]=P[v]+ P[w] }//if }//for } 算法7.15 时间复杂度:O(n2)

33 作 业 10 作业: 习题集 7.22 (page 49) 课后阅读: Floyd算法(教材7.6.2)


Download ppt "DFS和BFS用来干什么? 连通性 拓扑排序 关键路径."

Similar presentations


Ads by Google