最小连通分量

合集下载

的连通性与最小生成树算法的实现

的连通性与最小生成树算法的实现

的连通性与最小生成树算法的实现连通性问题是图论中的一个重要概念,指的是在一个无向图中,是否存在一条路径能够连接图中的任意两个顶点。

而最小生成树算法是解决连通性问题的一种常用方法,它可以找到一棵包含图中所有顶点的树,并且使得树的总权值最小。

本文将介绍的连通性的概念以及最小生成树算法的实现过程。

1. 连通性的概念在无向图中,连通性指的是图中的任意两个顶点之间存在一条路径。

如果一个图是连通的,则可以通过任意两个顶点之间的路径相互到达。

如果图不连通,则存在顶点无法通过路径到达其他顶点。

2. 最小生成树算法最小生成树(Minimum Spanning Tree, MST)是一种在无向连通图中生成一棵树的算法,这棵树包含了图中的所有顶点,并且使得树的总权值最小。

最常用的最小生成树算法有Prim算法和Kruskal算法。

3. Prim算法Prim算法是一种贪心算法,从一个顶点开始,逐步扩展生成最小生成树。

具体步骤如下:(1) 选择一个起始顶点,将该顶点加入最小生成树中,并且标记为已访问。

(2) 从已访问的顶点中选择与之相连的边中权值最小的边,将其连接的顶点加入最小生成树中,并且标记为已访问。

(3) 重复步骤(2),直到最小生成树包含了图中的所有顶点。

4. Kruskal算法Kruskal算法是一种基于边的贪心算法,逐步扩展生成最小生成树。

具体步骤如下:(1) 对图中的所有边按照权值进行排序。

(2) 依次从权值最小的边开始,将边加入最小生成树中,但要保证加入边的两个顶点不在同一个连通分量中。

(3) 重复步骤(2),直到最小生成树包含了图中的所有顶点或者边已经全部考虑完毕。

5. 最小生成树算法的实现最小生成树算法可以通过编程实现。

下面是一个使用Prim算法实现最小生成树的示例代码:```代码示例:function prim(graph):select an arbitrary vertex v from graphinitialize an empty set mstSet to store vertices included in minimum spanning treeinitialize a priority queue pq to store edges connected to the mstSetmark v as visitedenqueue all edges connected to v into pqwhile pq is not empty:dequeue an edge with minimum weight from pqif the edge connects two vertices not in mstSet:add the edge to the minimum spanning treemark the two vertices as visitedenqueue all edges connected to the selected vertex into pqreturn the minimum spanning tree```通过使用这个代码,可以根据给定的无向图找到其最小生成树。

能量均衡的最小连通支配集分布式算法

能量均衡的最小连通支配集分布式算法

能量均衡的最小连通支配集分布式算法能量均衡是无线传感器网络中一个重要的问题,通过调整传感器节点之间的能量消耗,实现能量的均衡分配,可以延长整个网络的生命周期。

连通支配集是指在无线传感器网络中,选择一些节点作为“关键节点”,保证网络中的任意节点都能够通过这些节点进行通信。

因此,能量均衡的最小连通支配集分布式算法的目标是找到一组节点,使得这些节点既能保证网络中的任意节点都能够通过它们进行通信,同时又能够实现能量的均衡分配。

1.初始化:每个节点将自己的能量水平广播给邻居节点,并收集邻居节点的能量信息。

2.节点选择:每个节点根据自己和邻居节点的能量信息,以及网络拓扑结构,计算出一个指标,作为选择节点的依据。

这个指标既要考虑能量水平,也要考虑节点在拓扑结构中的位置。

例如,一个节点的能量水平较高,但如果它的邻居节点中已经有其他节点被选为关键节点,那么它的指标可能会比较低。

3.关键节点选择:每个节点根据自己的指标,选择自己是否成为关键节点。

选择的策略可以是基于贪心算法,即选择指标最高的节点作为关键节点。

当节点决定成为关键节点时,它将向邻居节点发送成为关键节点的消息,并广播给整个网络。

4.连通支配集建立:每个节点根据收到的成为关键节点的消息,更新自己的关键节点集合。

根据关键节点集合,节点可以知道自己是否属于连通支配集。

如果节点属于连通支配集,则将自己的状态设置为“活动”,否则设置为“休眠”。

5.能量分配:每个节点根据自己的能量水平和相邻节点的状态,计算出一个能量分配方案。

根据方案,节点可以选择将自己的一部分能量分配给“活动”节点,以实现能量的均衡分配。

6.能量传输:根据能量分配方案,节点之间进行能量传输。

节点向邻居节点发送能量,并等待邻居节点确认能量的接收。

如果邻居节点接收到能量后水平不再低于一些阈值,那么节点可以将它从连通支配集中移除。

7.迭代调整:重复执行步骤5和6,直到能量的均衡分配达到一定的收敛状态。

可以根据网络中节点的能量变化情况,动态调整能量分配方案。

最小生成树简答题

最小生成树简答题

最小生成树简答题1. 最小生成树(Minimum Spanning Tree,简称MST)是图论中一个重要的概念,常用于解决网络设计、电力传输、城市规划等实际问题。

它可以被定义为一个连通图的子图,包含了图中所有的顶点,且边的权重之和最小。

2. 在许多实际应用中,我们需要找到连接所有节点的最小成本路径。

这个问题可以通过最小生成树算法来解决。

最小生成树算法的目标是找到一棵包含所有节点的树,并且边的权重之和最小化。

3. 最小生成树可以使用多种算法来计算,其中最著名的两种算法是Prim算法和Kruskal算法。

这两种算法分别属于贪心算法和并查集算法。

它们的核心思想是从图中的某个节点开始,逐步扩展生成树,直到覆盖了所有的节点。

4. Prim算法是一种贪心算法,它从图中的某个节点开始,每次选择一条与当前生成树相连的最短边,并将其加入生成树中。

通过这样的方式,不断扩展生成树,直到覆盖了所有的节点。

Prim算法的时间复杂度为O(V^2),其中V是节点的数量。

5. Kruskal算法是一种基于并查集的算法,它首先将所有的边按照权重从小到大进行排序。

然后依次遍历排序后的边,如果当前边的两个节点不在同一个连通分量中,就将这条边加入生成树中,并将这两个节点合并到同一个连通分量中。

通过不断地合并连通分量,最终生成包含所有节点的最小生成树。

Kruskal算法的时间复杂度为O(ElogE),其中E是边的数量。

6. 然而,最小生成树算法并不是唯一的解决方案。

在某些特定情况下,其他算法可能更加高效。

例如,在稀疏图中,Prim 算法的时间复杂度较高,可以使用Prim算法的优化版本Prim-Jarnik算法来解决。

7. 此外,最小生成树算法还有一些扩展应用,例如最小生成森林、最小生成树问题的变体等。

最小生成森林是指一个无向图中的若干个最小生成树的集合,它可以通过去掉一些边来得到。

而最小生成树问题的变体则是在原问题的基础上增加了一些约束条件,例如要求生成树中的边的数量满足某个范围。

并查集的应用探讨并查集在实际问题中的应用场景

并查集的应用探讨并查集在实际问题中的应用场景

并查集的应用探讨并查集在实际问题中的应用场景并查集的应用探讨:并查集在实际问题中的应用场景一、引言并查集(Disjoint Set)是一种常用的数据结构,主要用于解决集合的合并与查询问题。

它在实际问题中有着广泛的应用,本文将探讨并查集在实际问题中的应用场景。

二、并查集简介并查集是一种用于处理集合的数据结构,它支持三种操作:查找(Find)、合并(Union)和创建(MakeSet)。

其中查找用于找到元素所属的集合,合并用于将两个集合合并为一个集合,创建则用于创建一个新的集合。

三、应用场景一:网络连通性问题在计算机网络中,经常需要判断两个主机是否能够互相通信或者是否处于同一个网络中。

通过使用并查集来处理网络连通性问题,可以快速判断两个主机是否连通,从而进行相应的网络配置。

以一个局域网中多个主机的连通性判断为例,将每个主机看作一个节点,使用并查集来管理它们的连通关系。

当两个主机通过网络连接后,可以通过合并这两个主机所在的集合,即执行Union操作,将它们放在同一个连通分量中。

当需要判断两个主机是否连通时,只需要执行Find操作判断它们是否属于同一个连通分量即可。

四、应用场景二:社交网络中的朋友圈在社交网络中,人们经常需要查找自己与他人的关系是否存在连接,如朋友圈等。

通过利用并查集,可以快速构建和查询人际关系,判断两个人是否属于同一个朋友圈。

将每个人看作一个节点,使用并查集来管理它们的社交关系。

当两个人成为朋友时,即执行Union操作,将它们放在同一个朋友圈中。

当需要判断两个人是否属于同一个朋友圈时,只需要执行Find操作,判断它们是否属于同一个连通分量即可。

五、应用场景三:图的连通分量在图论中,连通分量是指无向图中的一组顶点,其中任意两个顶点之间都存在路径。

通过并查集,可以快速找到图中的连通分量,从而进行相应的图分析和处理。

以社交网络中用户之间的关注关系为例,将每个用户看作一个节点,使用并查集来管理它们的关系。

最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)

最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)

最⼩⽣成树---普⾥姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)最⼩⽣成树的性质:MST性质(假设N=(V,{E})是⼀个连通⽹,U是顶点集V的⼀个⾮空⼦集,如果(u,v)是⼀条具有最⼩权值的边,其中u属于U,v属于V-U,则必定存在⼀颗包含边(u,v)的最⼩⽣成树)普⾥姆算法(Prim算法)思路:以点为⽬标构建最⼩⽣成树1.将初始点顶点u加⼊U中,初始化集合V-U中各顶点到初始顶点u的权值;2.根据最⼩⽣成树的定义:从n个顶点中,找出 n - 1条连线,使得各边权值最⼩。

循环n-1次如下操作:(1)从数组lowcost[k]中找到vk到集合U的最⼩权值边,并从数组arjvex[k] = j中找到该边在集合U中的顶点下标(2)打印此边,并将vk加⼊U中。

(3)通过查找邻接矩阵Vk⾏的各个权值,即vk点到V-U中各顶点的权值,与lowcost的对应值进⾏⽐较,若更⼩则更新lowcost,并将k存⼊arjvex数组中以下图为例#include<bits/stdc++.h>using namespace std;#define MAXVEX 100#define INF 65535typedef char VertexType;typedef int EdgeType;typedef struct {VertexType vexs[MAXVEX];EdgeType arc[MAXVEX][MAXVEX];int numVertexes, numEdges;}MGraph;void CreateMGraph(MGraph *G) {int m, n, w; //vm-vn的权重wscanf("%d %d", &G->numVertexes, &G->numEdges);for(int i = 0; i < G->numVertexes; i++) {getchar();scanf("%c", &G->vexs[i]);}for(int i = 0; i < G->numVertexes; i++) {for(int j = 0; j < G->numVertexes; j++) {if(i == j) G->arc[i][j] = 0;else G->arc[i][j] = INF;}}for(int k = 0; k < G->numEdges; k++) {scanf("%d %d %d", &m, &n, &w);G->arc[m][n] = w;G->arc[n][m] = G->arc[m][n];}}void MiniSpanTree_Prim(MGraph G) {int min, j, k;int arjvex[MAXVEX]; //最⼩边在 U集合中的那个顶点的下标int lowcost[MAXVEX]; // 最⼩边上的权值//初始化,从点 V0开始找最⼩⽣成树Tarjvex[0] = 0; //arjvex[i] = j表⽰ V-U中集合中的 Vi点的最⼩边在U集合中的点为 Vjlowcost[0] = 0; //lowcost[i] = 0表⽰将点Vi纳⼊集合 U ,lowcost[i] = w表⽰ V-U中 Vi点到 U的最⼩权值for(int i = 1; i < G.numVertexes; i++) {lowcost[i] = G.arc[0][i];arjvex[i] = 0;}//根据最⼩⽣成树的定义:从n个顶点中,找出 n - 1条连线,使得各边权值最⼩for(int i = 1; i < G.numVertexes; i++) {min = INF, j = 1, k = 0;//寻找 V-U到 U的最⼩权值minfor(j; j < G.numVertexes; j++) {// lowcost[j] != 0保证顶点在 V-U中,⽤k记录此时的最⼩权值边在 V-U中顶点的下标if(lowcost[j] != 0 && lowcost[j] < min) {min = lowcost[j];k = j;}}}printf("V[%d]-V[%d] weight = %d\n", arjvex[k], k, min);lowcost[k] = 0; //表⽰将Vk纳⼊ U//查找邻接矩阵Vk⾏的各个权值,与lowcost的对应值进⾏⽐较,若更⼩则更新lowcost,并将k存⼊arjvex数组中for(int i = 1; i < G.numVertexes; i++) {if(lowcost[i] != 0 && G.arc[k][i] < lowcost[i]) {lowcost[i] = G.arc[k][i];arjvex[i] = k;}}}int main() {MGraph *G = (MGraph *)malloc(sizeof(MGraph));CreateMGraph(G);MiniSpanTree_Prim(*G);}/*input:4 5abcd0 1 20 2 20 3 71 2 42 3 8output:V[0]-V[1] weight = 2V[0]-V[2] weight = 2V[0]-V[3] weight = 7最⼩总权值: 11*/时间复杂度O(n^2)克鲁斯卡尔算法(Kruskal算法)思路:以边为⽬标进⾏构建最⼩⽣成树在边集中依次寻找最⼩权值边,若构建是不形成环路(利⽤parent数组记录各点的连通分量),则将其添加到最⼩⽣成树中。

强连通分量个数的最小值

强连通分量个数的最小值

强连通分量个数的最小值1. 引言在图论中,强连通分量是指图中的一组顶点,其中任意两个顶点都存在一条有向路径。

强连通分量个数的最小值是指在一个有向图中,最少需要将多少个顶点组成一个强连通分量。

本文将介绍强连通分量的概念、计算方法以及如何求解强连通分量个数的最小值。

2. 强连通分量的定义在有向图中,如果从顶点A到顶点B存在一条有向路径,同时从顶点B到顶点A也存在一条有向路径,则称顶点A和顶点B是强连通的。

如果一个有向图中的每个顶点都与其他所有顶点强连通,则该有向图被称为强连通图。

而强连通分量则是指有向图中的一组顶点,其中任意两个顶点都是强连通的,且不与其他顶点强连通。

3. 强连通分量的计算方法为了计算一个有向图的强连通分量,可以使用强连通分量算法,其中最常用的是Tarjan算法和Kosaraju算法。

3.1 Tarjan算法Tarjan算法是一种深度优先搜索算法,用于寻找有向图的强连通分量。

算法的基本思想是通过DFS遍历图中的每个顶点,并记录每个顶点的遍历次序和能够到达的最小顶点次序。

通过这些信息,可以判断顶点是否属于同一个强连通分量。

具体步骤如下:1.初始化一个空栈和一个空的遍历次序数组。

2.对于每个未遍历的顶点,进行深度优先搜索。

3.搜索过程中,记录每个顶点的遍历次序和能够到达的最小顶点次序,并将顶点加入栈中。

4.当搜索完成后,根据遍历次序和能够到达的最小顶点次序,可以确定每个顶点所属的强连通分量。

3.2 Kosaraju算法Kosaraju算法是另一种用于计算有向图强连通分量的算法。

算法的基本思想是通过两次深度优先搜索来确定强连通分量。

具体步骤如下:1.对原始图进行一次深度优先搜索,记录顶点的遍历次序。

2.对原始图的转置图(即将所有边的方向反转)进行一次深度优先搜索,按照遍历次序对顶点进行访问。

3.访问过程中,可以确定每个顶点所属的强连通分量。

4. 求解强连通分量个数的最小值要求解强连通分量个数的最小值,可以使用以下方法:1.使用Tarjan算法或Kosaraju算法计算有向图的强连通分量。

国家集训队2004论文集_汪汀

国家集训队2004论文集_汪汀
证明:⑴必要性 设 T 是最小 k 度限制生成树,则Ⅰ,Ⅱ显然成立。 以下证明 Ⅲ:由Ⅰ,Ⅱ
可知如果(+a1,-b2)和(+a2,-b1)都是 T 的可行交换,则有ω(b2)≤ω(a1),ω(b1)≤ω (a2),故ω(b1)+ω(b2)≤ω(a1)+ω(a2); 否则,或者(+a1,-b2)或者(+a2,-b1)不是 T 的 可行交换,根据引理 1,T’=T+{a1,a2}-{b1,b2}仍然是 T 的 k 度限制生成树,则ω (T)≤ω(T’),故ω(b1)+ω(b2)≤ω(a1)+ω(a2)。 ⑵充分性
综上,求最小 k 度限制生成树算法总的时间复杂度为 O(Vlog2V+E+kV)。
3、次小生成树
3.1、次小生成树的定义
设 G=(V,E,w)是连通的无向图,T 是图 G 的一个最小生成树。如果有另一棵树 T1,满 足不存在树 T’,ω(T’)<ω(T1) ,则称 T1 是图 G 的次小生成树。
3.2、求解次小生成树的算法
通过上述定理,我们就有了解决次小生成树问题的基本思路。 首先先求该图的最小生成树 T。时间复杂度 O(Vlog2V+E) 然后,求 T 的邻集中权值和最小的生成树,即图 G 的次小生成树。 如果只是简单的枚举,复杂度很高。首先枚举两条边的复杂度是 O(VE),再判断该交换是否 可行的复杂度是 O(V),则总的时间复杂度是 O(V2E)。这样的算法显得很盲目。经过简单的 分析不难发现,每加入一条不在树上的边,总能形成一个环,只有删去环上的一条边,才能 保证交换后仍然是生成树,而删去边的权值越大,新得到的生成树的权值和越小。我们可以 以此将复杂度降为 O(VE)。这已经前进了一大步,但仍不够好。 回顾上一个模型——最小度限制生成树,我们也曾面临过类似的问题,并且最终采用动态规 划的方法避免了重复计算,使得复杂度大大降低。对于本题,我们可以采用类似的思想。首 先做一步预处理,求出树上每两个结点之间的路径上的权值最大的边,然后,枚举图中不在

求最小树的计算方法

求最小树的计算方法

求最小树的计算方法最小生成树是指在一个连通的无向图中,找到一棵生成树,使得这棵生成树的边权之和最小。

最小生成树问题是图论中的经典问题,有着广泛的应用。

目前,最小生成树问题有两种经典的算法:Prim算法和Kruskal算法。

1. Prim算法Prim算法是一种贪心算法,它从一个点开始,每次选择一条最短的边连接到已经选中的点集合中的一个点,直到所有的点都被选中,构成一棵生成树。

具体实现步骤如下:(1)初始化:选定一个起始点,将该点加入已选中的点集合中,将与该点相连的边加入边集合中。

(2)重复以下步骤,直到所有点都被选中:- 从边集合中选出一条权值最小的边,该边所连接的点如果已经被选中,则跳过该边,否则将该点加入已选中的点集合中,将与该点相连的边加入边集合中。

时间复杂度为O(ElogV),其中E为边数,V为点数。

2. Kruskal算法Kruskal算法也是一种贪心算法,它从所有边中选取权值最小的边,如果该边所连接的两个点不在同一个连通分量中,则将这两个点所在的连通分量合并,直到所有点都在同一个连通分量中,构成一棵生成树。

具体实现步骤如下:(1)将所有边按照权值从小到大排序。

(2)初始化:将所有点看成一个连通分量。

(3)重复以下步骤,直到所有点都在同一个连通分量中:- 从排好序的边集合中选出一条权值最小的边,如果该边所连接的两个点在同一个连通分量中,则跳过该边,否则将这两个点所在的连通分量合并,将该边加入边集合中。

时间复杂度为O(ElogE),其中E为边数。

以上就是最小生成树的两种经典算法,它们都是基于贪心策略的,但具体实现方式略有不同。

在实际应用中,可以根据具体情况选择合适的算法。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

/*hdu3072Intelligence System 实质是缩点题意:两个直接或间接可以互相发送信息的点之间费用不计求建立一个网络(该网络可以实现从某一点访问遍所有点)最小费用
求强连通分量(相当于缩点),缩点后计算每个点最小入边的和
定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。

当DFN(u)=Low(u) 时,以u为根的搜索子树上所有节点是一个强连通分量。

*/
#include <iostream>
#include <algorithm>
using namespace std;
#define MAXN 50005
#define INF 00
int u[2*MAXN],v[2*MAXN],w[2*MAXN],head[2*MAXN],next[2*MAXN];
int DFN[MAXN],LOW[MAXN],instack[MAXN],Stap[MAXN],ID[MAXN],In[MAXN];
int Dindex,Stop,Bcnt;
void tarjan( int i)
{
int j;
DFN[i]=LOW[i]=++Dindex;
instack[i]= true ;
Stap[++Stop]=i;
for ( int e=head[i];e>=0;e=next[e])
{
j=v[e];
if (!DFN[j])
{
tarjan(j);
if (LOW[j]<LOW[i])
LOW[i]=LOW[j];
}
else if (instack[j]&&DFN[j]<LOW[i])
LOW[i]=DFN[j];
}
if (DFN[i]==LOW[i])
{
Bcnt++;
do
{
j=Stap[Stop--];
instack[j]= false ;
ID[j]=Bcnt;
} while (j!=i);
int n,m;
void pro()
{
Dindex=Stop=Bcnt=0;
memset(DFN,O, sizeof (DFN));
memset(ID,-1, sizeof (ID));
for ( int i=0;i<n;i++)
{
if (!DFN[i])tarjan(i);
}
}
int main()
{
while (scanf( "%d%d",&n,&m)==2)
{
for (int i=0;i<n;i++)head[i]=-1;
for (int i=0;i<m;i++)
{
scanf( "%d%d%d",&u[i],&v[i],&w[i]);
next[i]=head[u[i]];
head[u[i]]=i;
}
pro();
for (int i=0;i<n;i++)In[i]=INF;
for (int i=0;i<m;i++)
{
int s=ID[u[i]];
int t=ID[v[i]];
if (s!=t)
{
ln[t]=min(ln[t],w[i]);
}
}
int ret=0;
for (int i=0;i<n;i++)
if (ln[i]<INF)ret+=ln[i];
coutvvretvvendl;
}
}
[有向图强连通分量]
在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected) G的每两个顶点都强连通,称G是一个强连通图。

非强连通图有向图的极大强连通子图,称为(strongly connected components) 。

如果有向图强连通分量
下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。

{5},{6}也分别是两个强连通分量。

直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为O(NT+M)。

更好的方法是Kosaraju
算法或Tarjan算法,两者的时间复杂度都是O(N+M)。

本文介绍的是Tarjan算法。

[Tarjan 算法]
Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。

搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。

定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。

当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。

算法伪代码如下
tarjan 1(u)
1
1
1
1
1
1DFN[ u] =Low[ u] =++Index
1
1 //为节点u设定次序编号和Low初值
1 1 1 1Stack. push ( u) //将节点u压入栈中
1
1 1 1for each (u, v )in E //枚举每一条边
1
1 1 1 1if ( v is not visted ) //如果节点v未被访问过
1
1
1
tarjan (v) //继续向下找
1 1 1 1Low[ u] =min (Low[ u ] , Low [ v])
1
1
1
else if ( v in S ) //如果节点v还在栈内
1 1 1 1Low[ u] =min (Low[ u] , DFN [ v])
1 1 1 1if (DFN[ u]== L ow [ u]) //如果节点u是强连通分量的根
1 1 1repeat
1
1
1
1 1 1
1
v = S. pop //将v退栈,为该强连通分量中一个顶点1 1 1 1
1 1 1 1print v
1
1
1
1
1 1 1 1 1 } 1until (u== v )
1
1
1
1
1
1
1
接下来是对算法流程的演示。

从节点1开始DFS,把遍历到的节点加入栈中。

搜索到节点u=6时,DFN[6]=LOW[6],找到了一个强连通
分量。

退栈到u=v为止,{6}为一个强连通分量。

返回节点5,发现DFN[5]=LOW[5],退栈后{5}为一个强连通分量。

返回节点3,继续搜索到节点4,把4加入堆栈。

发现节点4向节点1有后向边,节点1还在栈中,所以
LOW[4]=1。

节点6已经出栈,(4,6)是横叉边,返回3,(3,4)为树枝边,所以LOW[3]=LOW[4]=1 。

继续回到节点1,最后访问节点2。

访问边(2,4),4还在栈中,所以LOW[2]=DFN[4]=5 。

返回1后,发现
DFN[1]=LOW[1],把栈中节点全部取出,组成一个连通分量{1,3,4,2}。

至此,算法结束。

经过该算法,求出了图中全部的三个强连通分量{1,3,4,2},{5},{6}。

可以发现,运行Tarjan算法的过程中,每个顶点都被访问了一次,且只进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度为O(N+M)。

求有向图的强连通分量还有一个强有力的算法,为Kosaraju算法。

Kosaraju是基于对有向图及其逆图两次
DFS的方法,其时间复杂度也是O(N+M)。

与Trajan算法相比,Kosaraju算法可能会稍微更直观一些。

但是Tarjan只用对原图进行一次DFS,不用建立逆图,更简洁。

在实际的测试中,Tarjan算法的运行效率
也比Kosaraju算法高30%左右。

此外,该Tarjan算法与求无向图的双连通分量(割点、桥)的Tarjan算法也有着很深的联系。

学习该Tarjan算法,也有助于深入理解求双连通分量的Tarjan算法,两者可以类比、组合理解。

求有向图的强连通分量的Tarjan算法是以其发明者Robert Tarjan命名的。

Robert Tarjan还发明了求双连
通分量的Tarjan算法,以及求最近公共祖先的离线Tarjan算法,在此对Tarjan表示崇高的敬意。

相关文档
最新文档