您的位置:首页 > 其它

强连通分量

2015-07-16 15:09 267 查看
有向图的强连通分量

在有向图中,u可达v不一定意味v可达到u,相互可达的节点则属于同一个强连通分量。

某节点的传递闭包为该节点所处的强连通分量和它所有后代所处的强连通分量的节点。

若有向图的所有节点同属于一个强连通分量,则称该有向图为强连通图。

在有向图中,若某子图中的任一对节点都互为可达,则该子图称为有向图的强连通分量。

计算有向图中强连通分量的方法如下:将有向图G中每条边的方向取反,得到图G的一个转置GT,G和GT中的强连通分量相同。将每个强连通分量缩成一个节点,就可以得到一个无环有向图Gscc。







1、Procedure Kosaraju(G);

{调用dfs(G)计算出每个节点的f[u];

计算图G的转置GT;

调用dfs(GT),在主循环中按照f[u]递减的顺序依次对每个未访问点执行dfs过程,则得到的每棵dfs树恰好对应于一个强连通分量;

}





V


0


1


2


3


4


5


6


7


8


9


10


11


12


d

1

17

2

3

4

5

7

8

9

19

21

23

20

f

16

18

15

14

13

6

12

11

10

26

22

24

25





f


26


25


24


22


18


16


15


14


13


12


11


10


6


v

9

12

11

10

1

0

2

3

4

6

7

8

5

0

0

0

0

1

2

2

2

2

2

3

3

2

模板:

const maxn=100;

var //map[x,i] 记录与点x邻接的第i个点的编号 map[x,0]记录和x邻接的点的个数

map,map1:array[1..maxn,0..maxn]of integer;

visit:array[1..maxn]of boolean; //记录该点是否被遍历过

list:array[1..maxn]of integer; //记录n个点的遍历次序

n,m,pos,scc:integer; //pos记录进入list数组的点的个数 scc记录强连通分量的个数

procedure init;

var i,x,y:integer;

begin

readln(n,m);

for i:=1 to m do

begin

readln(x,y);

inc(map[x,0]);

map[x,map[x,0]]:=y;

inc(map1[y,0]);

map1[y,map1[y,0]]:=x;

end;

end;

procedure dfs(p:integer);

var i,j,k:integer;

begin

visit[p]:=true; k:=map[p,0];

for i:=1 to k do

begin

j:=map[p,i];

if not visit[j] then dfs(j);

end;

inc(pos); list[pos]:=p;

end;

procedure dfs1(p:integer);

var i,j,k:integer;

begin

visit[p]:=true;

k:=map1[p,0];

for i:=1 to k do

begin

j:=map1[p,i];

if not visit[j] then dfs1(j);

end;

end;

procedure kosaraju;

var i,j,k:integer;

begin

fillchar(visit,sizeof(visit),false);

for i:=1 to n do if not visit[i] then dfs(i);

fillchar(visit,sizeof(visit),false); scc:=0;

for i:=pos downto 1 do //每深搜完一次,表示找完一个强连通图,增加scc

if not visit[list[i]] then begin dfs1(list[i]); inc(scc); end;

end;

begin

init;

pos:=0;

kosaraju;

writeln(scc);

end.

2、Tarjan算法

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。





算法流程演示:

1.从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时,DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连通分量。



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



3.返回节点3,继续搜索到节点4,把4加入堆栈。发现节点4向节点1有后向边,节点1还在栈中,所以LOW[4]=1。节点6已经出栈,(4,6)是横叉边,返回3,(3,4)为树枝边,所以LOW[3]=LOW[4]=1。



4.继续回到节点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)。

模板:

var

a:array [1..1000,1..1000] of longint;

low,dfn,c:array [1..1000] of longint;

v,f,ff:array [1..1000] of boolean;

i,j,m,n,x,y,d,ans:longint;

function min(x,y:longint):longint;

begin

if x>y then

exit(y);

exit(x);

end;

procedure tarjan(x:longint);

var

i:longint;

begin

inc(d);

low[x]:=d;

dfn[x]:=d;

f[x]:=true;

for i:=1 to c[x] do

begin

if not v[a[x,i]] then

begin

v[a[x,i]]:=true;

tarjan(a[x,i]);

low[x]:=min(low[x],low[a[x,i]]);

end else

begin

if f[a[x,i]] then

low[x]:=min(low[x],dfn[a[x,i]]);

end;

end;

if dfn[x]=low[x] then

inc(ans);

end;

begin

readln(n);

for i:=1 to n do

begin

read(x);

while x<>0 do

begin

inc(c[i]);

a[i,c[i]]:=x;

read(x);

end;

end;

for i:=1 to n do

if not v[i] then

begin

v[i]:=true;

tarjan(i);

end;

writeln(ans);

end.

有几道习题:

1./article/7675770.html

2./article/7675771.html

3./article/7675772.html

具体详见:http://download.csdn.net/detail/boyxiejunboy/8908487(更加详细哦)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: