算法导论--图算法Part2

本文深入讲解了图论中的核心算法,包括拓扑排序、强连通分量、最小生成树及单源最短路径等问题的解决方法。通过对比不同算法的特点,帮助读者理解其适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 拓扑排序:

1.基于入度的拓扑排序(Kahn's Algorithm)

(1) 算法思想:

Step1:计算所有点的入度(遍历所有的结点,将相邻结点的入度+1,时间复杂度为)

Step2:将所有入度为0点加入到一个队列;

Step3:取出队头元素,然后pop()
                  (1)count变量表示访问的结点个数
                  (2)top数组将每次出队的元素保存
                  (3)将这个点的所有相邻的点的入度-1;如果每个相邻的结点的入度减少为0,加入队列中;

Step4:重复步骤3直到队列

2)伪代码:

  1 while(!q.empty()){

  2    V v=q.front;

  3    top.push(v);

  4    q.pop();

  5    count++;

  6    for(itr=adj[v].begin();itr!=adj[v].end();itr++){

  7       if(--indegree[itr]==0)

  8       q.push(itr);

  9    }

10 }

3)应用:判断一个有向图是否有环

Step5:如果count和顶点个数,则将top数组中元素按序输出就是排序的结果,如果count不等于顶点个数,(不等情况下,一般是count>顶点个数)说明图中存在拓扑排序无法完成。

2.基于DFS(出度)的拓扑排序

1)算法思想:

Step1: 调用DFS(G)

Step2: 记录结点v的完成时间v.f,将v.f存放拓扑排序顺序数组topoSort[]中;

Step3: 拓扑排序得到的线性序列,即为topoSort[]的逆序;

2)伪代码:

二.强连通分量:

1.Kosaraju算法

1)算法思想:

Step1: 调用DFS(G),即伪拓扑排序

Step2: 对G进行转置得到GT

Step3:按照Step1中结点出的顺序,调用DFS(GT),得到若干搜索树每个搜就是一个强连通分量;

三.最小生成树

1.Prim算法

1)算法思想:

Step1:图的所有顶点集合为V;初始令集合u={s},v=V−u;

Step2:u,v组成的边中,选择代价最小的边(u0,v0),加入到最小生成树中,并把v0并入到集合u中;

Step3:重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止;

2)伪代码:

MST-PRIM(G,W,r):

1  for each u in G.V

2     u.key=+∞;

3     u.π=NULL;

4  r.key=0;

5  Q=G.V;

6  while Q!=NULL

7    u=EXTRACT-MIN(Q);

8    for each v in G.Adj[u];

9      if (v in Q) and w(u,v)<v.key

10        v.π=u

11        v.key=w(u,v); 

2.Kruskal算法

1)算法思想:

Step1: 把图中的所有边按代价从小到大排序

Step2: 把图中的n个顶点看成独立的n棵树组成的森林;

Step3: 按权值从小到大选择边,所选的边连接的两个顶点ui,vi,应属于两颗不同的树,则成为最小生成树的一

条边,并将这两颗树合并作为一颗树;

Step4: 重复Step3直到所有顶点都在一颗树内或者有n-1条边为止

2)伪代码:

MST-KRUSKAL(G,w):

1  A=NULL;

2  for each vertex v in G.V

3     MAKE-SET(v);

4  sort the edges of G.E into nondecreasing order by weight w

5  for each adges(u,v) in G.E,take in nondecreasing order by weight

6  if FIND-SET(v)!=FIND-SET(v)

7    A=A+{(u,v)}

8    UNION(u,v);

9    retrun A

四.单源最短路径

1.Bellman-Ford算法

1)算法思想:

Step1: 初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的

值设为无穷大(表示不可达);

Step2: 进行循环,循环下标为从1n1n等于图中点的个数),在循环内部,遍历所有的边,进行松弛计算;

Step3: 遍历途中所有的边(edgeuv)),判断是否存在这样情况:dv> d (u) + w(u,v),若存在则返回false

表示途中存在从源点可达的权为负的回路;

2)伪代码:

BELLMAN-FORD(G, w, s)

1  INITIALIZE-SINGLE-SOURCE(G, s)

2  for i ← 1 to |V[G]| - 1

3       do for each edge (u, v) ∈ E[G]

4              do RELAX(u, v, w)

5  for each edge (u, v) ∈ E[G]

6       do if d[v] > d[u] + w(u, v)

7             then return FALSE

8  return TRUE

2.Dijkstra算法

1)算法思想:

Step1: 初始时,S只包含源点,即S{v}v的距离为0U包含除v外的其他顶点,即:U={其余顶点},若v

U中顶点u有边,则<u,v>正常有权值,若u不是v的出边邻接点,则<u,v>权值为

Step2: U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是vk的最短路径长度);

Step3: k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距

离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权

    Step4:重复步骤Step2Step3直到所有顶点都包含在S.

2)伪代码:

DIJKSTRA(G,w,s)

1  INITIALIZE-SINGLE-SOURCE(G,s)

2  S=∅

3  Q=G.V

4  while Q!=∅

5    u=EXTRACT-MIN(Q)

6    S=S∪{u}

7    for each vertex v∈G.Adj[u]

8        RELAX(u,v,w)

3.有向无环图中的单源最短路径问题

(1)算法思想:

根据结点的拓扑排序次序来对带权重的有向无环图G进行边的松弛操作,时间复杂度O(V+E);

2)伪代码:

DAG-SHORTEST-PATH(G,w,s)

1  topologically sort the vertices of G

2  INITIALIZE-SINGLE-SOURCE(G,S)

3  for each vertex u,taken in topologically sorted order

4  for each vertex v∈G.Adj[u]

5  RELAX(u,v,w)


中间过程:

松弛操作:

RELAX(u,v,w)

1  if v.d>u.d+w(u,v)

2    v.d=u.d+w(u,v);

3    v.π=u;

初始化操作:

INITIALIZE-SINGLE-SOURCE(G,s)

1  for each vertex v ∈G.V

2   v.d=无穷大;

3   v.π=null;

4   s.d=0;

          (注:v.π表示前驱结点,v.d用于记录从源结点s到结点v的最短路径权重的上界

松弛过程为:测试是否可以对从sv的最短路径进行改善)

 

二. 比较分析

1.拓扑排序两种算法比较

(1)分别使用队列和栈存储;

(2)基于DFS的算法,加入结果集的条件是:点的出度为0;

(3)Kahn算法,加入结果集的条件是:点的度为0;

(4)一个有向图G=(V,E)是无环的当且仅当对其进行的深度优先搜索不产生后向边;

2.强连通分量

(1)算法时间复杂度:两次深度优先搜索计算图G的强连通分量,其中一次运行在G上,另一次

运行在GT上,所以复杂度为;

3.最小生成树

(1)两种算法都是基于贪心策略,每步通过寻找局部最优(安全边)来获得全局最优解;

(2)需要维持一个最小优先队列,使用普通的二叉堆,时间复杂度限制在,

Prim算法使用斐波那契堆则时间复杂度为

4.单源最短路径

(1)Dijkstra算法和用于有向无环图的最短路径算法对每条边仅松弛操作一次,

而BellmanFord算法对每条边松弛操作|V|-1次;

(2)时间复杂度:Dijkstra算法时间复杂度同样依赖最小队列的实现,使用二叉堆来实现最小优先

队列,则算法运行总时间为,使用斐波那契堆为;

5.相关定理

定理1.拓扑排序算法的正确性:

       对于任意一对不同的结点u,v∈V,如果G中包含一条从结点u到结点v的边,则v,f<u,f;

定理2.强连通分量算法的正确性:递归证明

定理3.最短路径的子路径也是最短路径;

定理4.Bellman-Ford算法正确性证明:

定理5.Dijkstra算法正确性证明:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值