Kosaraju 算法

Kosaraju 算法

一.算法简介

在计算科学中,Kosaraju的算法(又称为–Sharir Kosaraju算法)是一个线性时间(linear time)算法找到的有向图的强连通分量。它利用了一个事实,逆图(与各边方向相同的图形反转, transpose graph)有相同的强连通分量的原始图。

有关强连通分量的介绍在之前Tarjan 算法中:Tarjan Algorithm

逆图(Tranpose Graph ):

我们对逆图定义如下:

GT=(V, ET),ET={(u, v):(v, u)∈E}}

上图是有向图G , 和图G的逆图 G

摘录维基百科上对Kosaraju Algorithm 的描述:

(取自https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm)

  • For each vertex u of the graph, mark u as unvisited. Let L be empty.
  • For each vertex u of the graph do Visit(u), where Visit(u) is the recursive subroutine:
    If u is unvisited then:

    1. Mark u as visited.
    2. For each out-neighbour v of u, do Visit(v).
    3. Prepend u to L.
    Otherwise do nothing.
  • For each element u of L in order, do Assign(u,u) where Assign(u,root) is the recursive subroutine:
    If u has not been assigned to a component then:

    1. Assign u as belonging to the component whose root is root.
    2. For each in-neighbour v of u, do Assign(v,root).
    Otherwise do nothing.

通过以上的描述我们发现,Kosaraju 算法就是分别对原图G 和它的逆图 GT 进行两遍DFS,即:

1).对原图G进行深度优先搜索,找出每个节点的完成时间(时间戳)

2).选择完成时间较大的节点开始,对逆图GT 搜索,能够到达的点构成一个强连通分量

3).如果所有节点未被遍历,重复2). ,否则算法结束;

二.算法图示

上图是对图G,进行一遍DFS的结果,每个节点有两个时间戳,即节点的发现时间u.d和完成时间u.f

我们将完成时间较大的,按大小加入堆栈

1)每次从栈顶取出元素

2)检查是否被访问过

3)若没被访问过,以该点为起点,对逆图进行深度优先遍历

4)否则返回第一步,直到栈空为止

[ATTENTION] : 对逆图搜索时,从一个节点开始能搜索到的最大区块就是该点所在的强连通分量。

从节点1出发,能走到  2 ,3,4 , 所以{1 , 2 , 3 , 4 }是一个强连通分量

从节点5出发,无路可走,所以{ 5 }是一个强连通分量

从节点6出发,无路可走,所以{ 6 }是一个强连通分量

自此Kosaraju Algorithm完毕,这个算法只需要两遍DFS即可,是一个比较易懂的求强连通分量的算法。

三.算法复杂度

邻接表:O(V+E)

邻接矩阵:O(V2)

该算法在实际操作中要比Tarjan算法要慢

四.算法模板&注释代码

 1 #include "cstdio"
 2 #include "iostream"
 3 #include "algorithm"
 4
 5 using namespace std ;
 6
 7 const int maxN = 10010 , maxM = 50010;
 8
 9 struct Kosaraju { int to , next ; } ;
10
11 Kosaraju E[ 2 ][ maxM ] ;
12 bool vis[ maxN ];
13 int head[ 2 ][ maxN ] , cnt[ 2 ] , ord[maxN] , size[maxN] ,color[ maxN ];
14
15 int tot , dfs_num  , col_num , N , M  ;
16
17 void Add_Edge( int x , int y , int _ ){//建图
18          E[ _ ][ ++cnt[ _ ] ].to = y ;
19          E[ _ ][ cnt[ _ ] ].next = head[ _ ][ x ] ;
20          head[ _ ][ x ] = cnt[ _ ] ;
21 }
22
23 void DFS_1 ( int x , int _ ){
24          dfs_num ++ ;//发现时间
25          vis[ x ] = true ;
26          for ( int i = head[ _ ][ x ] ; i ; i = E[ _ ][ i ].next ) {
27                  int temp = E[ _ ][ i ].to;
28                  if(vis[ temp ] == false) DFS_1 ( temp , _ ) ;
29          }
30          ord[(N<<1) + 1 - (++dfs_num) ] = x ;//完成时间加入栈
31 }
32
33 void DFS_2 ( int x , int _ ){
34          size[ tot ]++ ;// 强连通分量的大小
35          vis[ x ] = false ;
36          color[ x ] = col_num ;//染色
37          for ( int i=head[ _ ][ x ] ; i ; i = E[ _ ][ i ].next ) {
38                  int temp = E[ _ ][ i ].to;
39                  if(vis[temp] == true) DFS_2(temp , _);
40          }
41 }
42
43 int main ( ){
44          scanf("%d %d" , &N , &M );
45          for ( int i=1 ; i<=M ; ++i ){
46                  int _x , _y ;
47                  scanf("%d %d" , &_x , &_y ) ;
48                  Add_Edge( _x , _y , 0 ) ;//原图的邻接表
49                  Add_Edge( _y , _x , 1 ) ;//逆图的邻接表
50          }
51          for ( int i=1 ; i<=N ; ++i )
52                  if ( vis[ i ]==false )
53                           DFS_1 ( i , 0 ) ;//原图的DFS
54
55          for ( int i = 1 ; i<=( N << 1) ; ++i ) {
56                  if( ord[ i ]!=0 && vis[ ord[ i ] ] ){
57                           tot ++ ; //强连通分量的个数
58                           col_num ++ ;//染色的颜色
59                           DFS_2 ( ord[ i ] , 1 ) ;
60                  }
61          }
62
63          for ( int i=1 ; i<=tot ; ++i )
64                  printf ("%d ",size[ i ]);
65          putchar (‘\n‘);
66          for ( int i=1 ; i<=N ; ++i )
67                  printf ("%d ",color[ i ]);
68          return 0;
69 }

2016-09-18 00:16:19

(完)

时间: 2024-09-29 00:13:43

Kosaraju 算法的相关文章

Kosaraju算法解析: 求解图的强连通分量

1. 定义 连通分量:在无向图中,即为连通子图. 上图中,总共有四个连通分量.顶点A.B.C.D构成了一个连通分量,顶点E构成了一个连通分量,顶点F,G和H,I分别构成了两个连通分量. 强连通分量:有向图中,尽可能多的若干顶点组成的子图中,这些顶点都是相互可到达的,则这些顶点成为一个强连通分量. 上图中有三个强连通分量,分别是a.b.e以及f.g和c.d.h. 2. 连通分量的求解方法 对于一个无向图的连通分量,从连通分量的任意一个顶点开始,进行一次DFS,一定能遍历这个连通分量的所有顶点.所以

Kosaraju 算法检测有向图的强连通性

给定一个有向图 G = (V, E) ,对于任意一对顶点 u 和 v,有 u --> v 和 v --> u,亦即,顶点 u 和 v 是互相可达的,则说明该图 G 是强连通的(Strongly Connected).如下图中,任意两个顶点都是互相可达的. 对于无向图,判断图是否是强连通的,可以直接使用深度优先搜索(DFS)或广度优先搜索(BFS),从任意一个顶点出发,如果遍历的结果包含所有的顶点,则说明图是强连通的. 而对于有向图,则不能使用 DFS 或 BFS 进行直接遍历来判断.如下图中,

Kosaraju算法详解

Kosaraju算法可以求出有向图中的强连通分量个数,并且对分属于不同强连通分量的点进行标记.它的算法描述较为简单: (1) 第一次对图G进行DFS遍历,并在遍历过程中,记录每一个点的退出顺序.以下图为例:     如果以1为起点遍历,访问结点的顺序如下: 结点第二次被访问即为退出之时,那么我们可以得到结点的退出顺序: (2)倒转每一条边的方向,构造出一个反图G’.然后按照退出顺序的逆序对反图进行第二次DFS遍历.我们按1.4.2.3.5的逆序第二次DFS遍历: 访问过程如下: 每次遍历得到的那

codevs1506传话(kosaraju算法)

- - - - - - - - 一个()打成[] 看了一晚上..... /* 求强连通分量 kosaraju算法 边表存图 正反构造两个图 跑两边 分别记下入栈顺序 和每个强连通分量的具体信息 */ #include<iostream> #include<cstring> #include<cstdio> #include<stack> #define maxn 1010 #define maxx 10010 using namespace std; int

Kosaraju 算法查找强连通分支

有向图 G = (V, E) 的一个强连通分支(SCC:Strongly Connected Components)是一个最大的顶点集合 C,C 是 V 的子集,对于 C 中的每一对顶点 u 和 v,有 u --> v 和 v --> u,亦即,顶点 u 和 v 是互相可达的. 实际上,强连通分支 SCC 将有向图分割为多个内部强连通的子图.如下图中,整个图不是强连通的,但可以被分割成 3 个强连通分支. 通过 Kosaraju 算法,可以在 O(V+E) 运行时间内找到所有的强连通分支.Ko

CSU 1612: Destroy Tunnels 强连通分量 Kosaraju算法

链接 :  http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1612 题意: 给一个矩阵A 大小N*N, B = A^1 + A^2 + A^3 + .... A^n , B中是否存在非0项. 题目可以转化为 N个点 编号为1-n, 对于任意点v,经过一些任意的步长到达u (u为所有点集的任意一个).离散数学里有图的矩阵相关知识 A^k代表了矩阵中从i到j的步长为k的方法数. 题目就是求整个图是否强连通. 学习了 Kosaraju算法 可以轻

强联通分量之kosaraju算法

首先定义:强联通分量是有向图G=(V, E)的最大结点集合,满足该集合中的任意一对结点v和u,路径vu和uv同时存在. kosaraju算法用来寻找强联通分量.对于图G,它首先随便找个结点dfs,求出每个节点最后一次访问的时间戳f(x),然后我们建立反图Gt,接着根据倒序的时间戳来dfs每个节点,每次dfs到的结点集合就是一个强联通分量.事实上这个算法的思想和拓扑排序类似. 我们来证明它(注意这里面的图指原图,而不是反图): 引理:对于G中的两个强联通分量C和C',若点u属于C,点v属于C',且

[图论] 有向图强连通分量 (kosaraju算法,Tarjan算法)

记录自己的想法:在有向图中,如果一些顶点中任意两个顶点都能互相到达(间接或直接),那么这些顶点就构成了一个强连通分量,如果一个顶点没有出度,即它不能到达其他任何顶点,那么该顶点自己就是一个强连通分量.在用kosaraju算法和Tarjan算法求强连通分量的时候,就是给所有的顶点分组染色,同一种颜色的顶点在同一个强连通分量中,记录有多少种颜色(有多少个强联通分量),每个顶点属于哪种颜色(每个顶点在哪个强连通分量重).在同一个强连通分量中的所有顶点可以缩为一个顶点,然后根据缩点构造DAG(有向无环图

kosaraju算法应用(一)

题目链接:POJ 2186 解题思路: kosaraju算法,本以为要缩点,但是题目只要求找到拓扑排序的一个唯一的头,可以水过--通过计算强连通分量的出度. 代码: #include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n,m,v[10005],num[1000