图的遍历
深度优先搜索(DFS)
步骤:1.在访问图中某一起始顶点v后,由v出发,访问它的任一邻接顶点 w1;
2.再从W1出发,访问与W邻接但还未被访问过的顶点W2;
3.然后再从W2出发,进行类似的访问,……
4.如此进行下去, 直至到达所有的邻接顶点都被访问过的顶点 u为止。
5.接着,退回一步,退到前一次刚访问过的顶点,看是否还有其它没有被访问的邻接顶点。如果有,则访问此顶点,之后再从此顶点出发,进行与前述类似的访问; 如果没有,就再退回一步进行搜索。重复上述过程,直到连通图中所有顶点都被访问过为止。

邻接矩阵实现DFS
设一个辅助数组visited[n],visited[i]表示第i个结点是否被访问,若未被访问,则值为0,若被访问,则值为1。

用邻接矩阵来表示图,遍历图中每一个顶点都要从头扫描该顶点所在行,时间复杂度为O(n^2)。
用邻接表来表示图,虽然有2e个表结点,但只需扫描e个结点即可完成遍历,加上访问n个头结点的时间,时间复杂度为O(n+ e)。
广度优先搜索(BFS)
步骤:从图的某一结点出友, 首先依次访问该结点的所有邻接点Vi, Vi2, ... Vin再按这些顶点被访问的先后次序依次访问与它们相邻接的所有未被访问的顶点,重复此过程,直至所有顶点均被访问为止。

邻接表实现BFS
同样设一个辅助数组visited[n],visited[i]表示第i个结点是否被访问,若未被访问,则值为0,若被访问,则值为1。同时用一个循环队列,通过入队出队访问邻接点。

如果使用邻接矩阵,则BFS对于每一个被访问到的顶点,都要循环检测矩阵中的整整一行( n个元素),总的时间代价为O(n)。
用邻接表来表示图,虽然有2e个表结点,但只需扫描e个结点即可完成遍历,加上访问n个头结点的时间,时间复杂度为O(n+e)。
DFS与BFS比较:
(1)空间复杂度相同,都是O(n)(借用了堆栈或队列) ;
(2)时间复杂度只与存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关。
图的应用
最小生成树
相关知识
生成树:所有顶点均由边连在一起,但不存在回路的图。
一个图可以有许多棵不同的生成树。
生成树的特点:
(1)生成树的顶点个数与图的顶点个数相同;
(2)生成树是图的极小连通子图,去掉一条边则非连通;
(3)一个有n个顶点的连通图的生成树有n-1条边;
(4)在生成树中再加一条边必然形成回路。
(5)生成树中任意两个顶点间的路径是唯一的;
含n个顶点,n-1条边的图不一定是生成树。
设图G=(V, E)是个连通图,当从图任一顶点出发遍历图G时,将边集E(G)分成两个集合(T(G)和B(G)。其中T(G)是遍历图时所经过的边的集合,B(G) 是遍历图时未经过的边的集合。显然,G1VT)是图G的极小连通子图。即子图G1是连通图G的生成树。

最小生成树: 给定一个无向网络,在该网的所有生成树中,使得各边权值之和最小的那棵生成树称为该网的最小生成树,也叫最小代价生成树。

构造最小生成树
构造最小生成树的算法很多,其中多数算法都利用了MST的性质。
MST:设N= (V, E)是一一个连通网,U是顶点集V的一个非空子集。若边(u,v)是一条具有最小权值的边,其中u∈U, v∈V-U,则必存在一棵包含边(u, v)的最小生成树。
在生成树的构造过程中,图中n个顶点分属两个集合:
已落在生成树上的顶点集: ∪
尚未落在生成树上的顶点集: V-U
接下来则应在所有连通U中顶点和V-U中顶点的边中选取权值最小的边。
Prim算法
算法思想:选择点,每次选择最小权值的边加入到生成树中,然后再更新权值,如此反复,保证每次最优来达到最优解。
(1)设N=(V,E)是连通网,TE是N上最小生成树中边的集合。
(2)初始令U={u0}, (u0∈V), TE={}。
(3)在所有u∈U, V∈V-U的边(u, v)∈E中,找一条代价最小的边(uo, v0)。
(4)将(u0,v0)并入集合TE,同时v0并入U。
(5)重复上述操作直至U=V为止,则T=(V, TE)为N的最小生成树。
时间复杂度为O(n^2)
Kruskal 算法
算法思想:选择边,优先先选出全体边里最短的那几条,然后如果各分量还没连起来,就继续选择剩余没被选择的边里最短的,直到全部节点都连接在一起。
(1)设连通网N= (V, E),令最小生成树初始状态为只有n个顶点,而无边的非连通图T=(V,{ }),每个顶点自成一个连通分量。
(2)在E中选取代价最小的边,若该边依附的顶点落在T中不同的连通分量上(即:不能形成环)则将此边加入到T中;否则,舍去此边,选取下一条代价最小的边。
(3)依此类推,直至T中所有顶点都在同一连通分量上为止。
时间复杂度为O(e*loge)e为边数
最短路径
在有向网中A点(源点)到达B点(终点)的多条路径中,寻找一条各边权值之和最小的路径,即最短路径。
最短路径不同于最小生成树,并不需要把所有点包括进来。
两点间最短路径

Dijkstra算法
(1)初始化:先找出从源点v0到各终点vk的直达路径(v0,vk),即通过一条弧到达的路径。
(2)选择:从这些路径中找出一条长度最短的路径(vo,u)。
(3)更新:然后对其余各条路径进行适当调整:
若在图中存在弧(u,Vk) ,且(vo,u) + (u,vk) < (vo,vk),则以路径(vo,u,vk) 代替(vo,vk),在调整后的各条路径中,再找长度最短的路径,依此类推。
1.把V分成两组:(1)S:已求出最短路径的集合,(2)T=V-S,尚未确定最短路径的集合
2.将T中顶点按最短路径递增的次序加入到S当中,保证:(1)从源点v0到S中各顶点的最短路径长度都不大于从v0到T中任何顶点的最短路径长度。(2)每个顶点对应一个距离值,S中的顶点:从v0到顶点的最短路径长度。T中顶点:从v0到顶点且只包括S中顶点作为中间顶点项的最短路径长度。
某源点到其他各点的最短路径

Floyd算法
算法思想:逐个顶点试探,从vi到vj的所有可能路径,选出一条最短的路径
求最短路径步骤:(1)初始设置一个n阶方阵,令其对角线元素为0,若存在弧<vi,vj>,则对应元素为权值,否则为∞,

(2)逐步试着在原直接路径中增加中间顶点,若加入中间顶点后路径变短,则修改之,否则不变。所有顶点试探完毕,算法结束。

拓扑排序
有向无环图:无环的有向图,称为DAG图。
AOV网:用一个有向图表示一个工程的各子工程及其相互制约的关系, 其中以顶点表示活动,弧表示活动之间的优先制约关系,称这种有向图为顶点表示活动的网,简称AOV网。
AOE网:用一个有向图表示一个工程的各子工程及其相互制约的关系,以弧表示活动,以顶点表示活动的开始或结束事件,称这种有向图为边表示活动的网,简称为AOE网。
如图例:

AOV网中不能有回路,因为活动的先决条件不能为自己。
拓扑排序:在AOV网没有回路的前提下,我们将全部活动排列成一个线性序列,使得若AOV网中有弧<i, j>存在,则在这个序列中,i一定排在j的前面,具有这种性质的线性序列称为拓扑有序序列,相应的拓扑有序排序的算法称为拓扑排序。
拓扑排序方法:(1)在有向图中选取一个没有前驱的顶点输出之。(2)从图中删除该顶点和所有以它为尾的弧。(3)重复上述步骤,直到全部顶点都已输出或当图中不存在没有前驱的顶点为止。

拓扑排序的应用:
检测AOV网中是否存在环:
对有向图构造其顶点的拓扑有序序列,若网中所有顶点都在它的拓扑有序序列中,则该AOV网必定不存在环。
如图例:

由于C3有前驱结点C8,故C3无法删除,则排序后会剩下:

关键路径
把工程计划表示为边表示活动的网络,即AOE网,用顶点表示事件,弧表示活动,弧的权表示活动持续时间。事件表示在它之前的活动已经完成,在它之后的活动可以开始。

对于AOE网,我们关注两个问题:
(1)完成整项工程需要多长时间。
(2)哪些活动是影响工程进度的关键。
关键路径——路径长度最长的路径
确定关键路径:【1】(1)ve(vj)——表示事件vj发生的最早时间。
(2)vl(vj)——表示事件vj发生的最晚时间。
(3)e(i)——表示活动ai的最早开始时间。
(4)l(i)——表示活动ai的最晚开始时间。
(5)l(i)-e(i)——表示完成活动ai的时间余量。
(6)关键活动——关键路径上的活动,即I(i)==e(i)【l(i)-e(i)==0】的活动。
【2】找I(i)==e(i),设活动ai用弧<j,k>表示,持续时间为Wjk,则有e(i)=ve(j)和l(i)=vl(k)-Wjk。
【3】求ve(j)和vl(j):(1) 从ve(1) = 0开始向前递推ve(j) = Max{ve(i) + Wij}, < i,j>∈T,2≤j≤n其中T是所有以j为头的弧的集合。(2)从vl(n) = ve(n)开始向后递推vl(i) = Min{vl(j) - Wij}, < i,j >∈S,1≤i≤n-1其中S是所有以i为尾的弧的集合。
例图:求图中关键路径:

1.求ve(i),vl(j):
ve(1)为开始显然为0,ve(2)=MAX{0+6}=6,ve(3)=MAX{0+4}=4,ve(4)=MAX{0+5}=5,ve(5)=MAX{6+1,4+1}=7,ve(6)=MAX{5+2}=7,ve(7)=MAX{7+9}=16,ve(8)=MAX{7+7,7+4}=14,ve(9)=MAX{16+2,14+4}=18。
由ve(9)=18,得到vl(9)=18,则vl(8)=min{18-4}=14,vl(7)=min{18-2}=16,vl(6)=min{14-4}=10,vl(5)=min{16-9,14-7}=7,vl(4)=min{10-2}=8,vl(3)=min{7-1}=6,vl(2)=min{7-1}=6,vl(1)=min{6-6,6-4,8-5}=0。

2.求e(i)和l(i):
易得e(1)=ve(1)=0, e(2)=ve(1)=0, e(3)=ve(1)=0, e(4)=ve(5)=6,e(5)=ve(3)=4, e(6)=ve(4)=5, e(7)=ve(5)=7, e(8)=ve(5)=7, e(9)=ve(6)=7,e(10)=ve(7)=16,e(11)=ve(8)=14。
l(1)=vl(2)-W12=6-6=0, l(2)=vl(3)-W13=6-4=2, l(3)=vl(4)-W14=8-5=3, l(4)=vl(5)-W25=7-1=6, l(5)=vl(5)-W35=7-1=6, l(6)=vl(6)-W46=10-2=8, l(7)=vl(7)-W57=16-9=7, l(8)=vl(8)-W58=14-7=7, l(9)=vl(8)-W68=14-4=10, l(10)=vl(9)-W79=18-2=16, l(11)=vl(9)-W89=18-4=14。
3.计算l(i)-e(i):
另p(i)表示,则p(1)=0,p(2)=2,p(3)=3,p(4)=0,p(5)=2,p(6)=3,p(7)=0,p(8)=0,p(9)=3,p(10)=0,p(11)=0。如图所示:

则a1,a4,a7,a8,a10,a11为关键活动,关键活动构成的路径为关键路径。
1、若网中有几条关键路径,则需加快同时在几条关键路径上的关键活动。如: a11、a10、a8、 a7。
2、如果一个活动处于所有的关键路径上,那么提高这个活动的速度,就能缩短整个工程的完成时间。如: a1、a4。
3、处于所有的关键路径上的活动完成时间不能缩短太多,否则会使原来,个的关键路径变成不是关键路径。这时,必须重新寻找关键路径。如: a1由6天变成3天,就会改变关键路径。
本文基于C语言介绍图的遍历与应用。遍历方法有深度优先搜索(DFS)和广度优先搜索(BFS),并分析了不同存储结构下的时间复杂度。应用方面,涵盖最小生成树(Prim、Kruskal算法)、最短路径(Dijkstra、Floyd算法)、拓扑排序和关键路径等内容,还给出了相关算法思想和步骤。

被折叠的 条评论
为什么被折叠?



