图算法
最小生成树
Kruskal 算法
- 思路: 寻找安全边的方法是,在所有连接森林中两棵不同树的边里面,寻找最小的边 ( u , v ) ( u , v ) (u,v)
- 时间复杂度:时间复杂度为O(ElgV)
- 主要取决于边数,适合稀疏图。
Prim算法
- 思路:Prim 算法具有一个性质是集合A中的边总是构成一棵树。这棵树从某一顶点 r 开始,每一次扩展所加入的边必须是使得树的总权重增加最少的边,最终形成图的一棵最小生成树。
- 时间复杂度:O(ElgV)[二叉堆], O(E+VlgV)[斐波那契堆][DecreaseKey操作]
- 与图中的边数无关,适合稠密图。
单源最短路径
Bellman-Ford算法:
- 顶点为V,边为E的图
- 对每条边松弛|V|-1次
- 边权可以为负值
- 若存在一个可以从源结点到达的权值为负值的环路,算法返回False
- 时间复杂度:O(VE)
DAG-SHORTEST-PATHS
- 有向无环图单源最短路径
- 算法首先对有向无环图进行拓扑排序
- 即使存在权值为负的边,也因为没有权值为负的环路,最短路径是存在的
- 时间复杂度: O ( V + E ) O(V+E) O(V+E) 对于邻接表表示的图,这个时间为线性级
Dijkstra算法
- 顶点为V,边为E的图
- 对每条边仅松弛1次
- 边权不可为负
- 运行过程维护一组结点集合S
- 使用贪心策略,每次选择集合V-S中最“近”的结点加入集合S
- 利用结点编号维持最小优先队列,时间复杂度为:O(V2+E)=O(V2)
- 如果是稀疏图,可以利用二叉堆实现最小优先队列,时间复杂度:O(ElgV)
- 利用斐波那契堆实现最小优先队列,时间复杂度:O(VlgV+E)
所有结点对的最短路径问题
Floyd-Warshall算法
- 顶点为V,边为E的图
-
使用动态规划公式解决所有结点对最短路径问题
-
时间复杂度:O(V3)
-
可以有负权值的边,但不可以有负权值环路
-
δ ( i , j ) δ(i,j) δ(i,j)表示结点 i i i到结点 j j j的最短路径权重
-
Johnson算法
- 用于稀疏图
- 要么返回一个包含所有结点对的最短路径权重的矩阵,要么报告输入图包含一个权重为负值的环路
- 通过重新赋值来生成非负权重(不改变最短路径), h : V → R h:V\rightarrow R h:V→R为任意函数:将结点映射到实数上, w ^ ( p ) = w ( p ) + h ( v 0 ) − h ( v k ) \hat{w}(p)=w(p)+h(v_0)-h(v_k) w^(p)=w(p)+h(v0)−h(vk)
- 重新赋值不改变环路的权重
- 对图进行预处理,在O(VE) 时间内计算出新权重 w ^ \hat{w} w^
- 时间复杂度:斐波那契堆:O(V2lgV+VE),二叉最小堆:O(VElgV)
- 运行中需要使用Dijkstra算法和Bellman-Ford算法作为自己的子程序
最大流
增广路径是残存网络中一条从源点s到汇点t的简单路径
Ford-Fulkson算法
- 算法运行时间取决于如何找到增广路径
- f ∗ f^* f∗表示转换后网络的一个最大流,则运行时间为 O ( E ∣ f ∗ ∣ ) O(E|f^*|) O(E∣f∗∣)
- 使用BFS或者DFS在残存网络找一条增广路径的时间为 O ( V + E ) O(V+E) O(V+E)
Edmonds-Karp算法
- 使用广度优先搜索BFS寻找增广路径的Ford-Fulkerson方法就是Edmonds-Karp算法
- 运行时间:O(VE2)
- 运行在流网络G=(V,E)上,算法执行的流量递增操作总次数为 O ( V E ) O(VE) O(VE)
例题
-
我们怎样才能使用 Floyd-Warshall 算法的输出来检测权重为负值的环路?
若图中有负环,假设节点 i 为负值环路中的一个节点,在循环中会更新节点 i 到自身的距离,对于大于负值环路中的所有节点序号k,会使得 d i i k d_{ii}^k diik为负值。 -
假定在一个权重函数为 W 的有向图图 G 上运行 Johnson 算法。
证明:如果图 G 包含一条权重为0 的环路 c,那么对于环路 c 上的每条边 (u, v), w ^ ( u , v ) = 0 \hat{w}(u,v)=0 w^(u,v)=0
由于重新赋值不会改变环路的权重,故重新赋值后此环路的权重仍为0,又由于重新赋值后每条边的权重为非负权重,故对于环路 c 上的每条边 (u, v),有 w ^ ( u , v ) = 0 \hat{w}(u,v)=0 w^(u,v)=0。 -
给定 G = (V,E) 是一带权重且没有权重为负值的环路的有向图,对于所有的结点 v ∈ V,从源结点 s到结点 v 之间的最短路径中,包含边的条数的最大值为 m。请对算法 BELLMAN-FORD 进行简单修改,可以让其在 m+1 遍松弛操作之后终止,即使 m 不是事先知道的一个数值。
用一个数组记录每次循环中每个节点的v.d值,若在本轮循环中发现所有节点的v.d值较上一轮均不变,则跳出循环,算法结束。 -
请举出一个包含负权重的有向图,使得 Dijkstra 算法在其上运行时将产生不正确的结果。为什么在有负权重的情况下,这一定理的证明不成立?
Dijkstra将认为s到u的最短路径长度为2而不是1。
假设 x , y x,y x,y为 s → u s\rightarrow u s→u的最短路径上的两个结点,且s,x已经在集合S中,当对x的邻居结点y进行松弛操作时, y . d = δ ( s , y ) y.d=δ(s,y) y.d=δ(s,y),但由于有负边存在,此时 δ ( s , y ) ≤ δ ( s , u ) δ(s,y)≤δ(s,u) δ(s,y)≤δ(s,u)不成立,即 y . d ≤ u . d y.d≤u.d y.d≤u.d不成立,则定理的证明不成立 -
假定图中的边权重全部为整数,且在范围 1 ∼ |V| 内,在此情况下,Kruskal 算法最快能多快?如果变得权重取值范围在 1 到某个常数 W 之间呢?
边权重是 到 范围内的整数,我们可以通过使用计数排序在线性时间内按权重对边进行排序,从而使 Kruskal 算法在O(Eα(V)) 时间内运行。 如果边权重是由常数限定的整数,可以采用相同的方法,因为运行时算法需要决定边是否加入不相交的森林,而这与边的权重无关。
-
假定图中的边权重全部为整数,且在范围 1 ∼ |V| 内,在此情况下,Prim 算法最快能多快?
先选择一个顶点作为树的根节点,把这个根节点当成一棵树,选择图中距离这棵树最近但是没有被树收录的一个顶点,把他收录在树中,并且保证不构成回路,按照这样的方法,把所有的图的顶点一一收录进树中。使用 vEB 树来处理 Prim 算法中的优先队列这一问题的时间复杂度为O(EloglogV)