有向圖的連通性


something important

  • 力求描述性語言關鍵,簡練,避免大段文字轟炸
  • 部分內容來自網絡

零.強連通圖,強連通分量

  • 強連通圖定義:在有向圖G中,如果任意兩個不同的頂點相互可達,則稱該有向圖是強連通的。

舉個例子:下圖有三個子圖(強連通分量):{1,4,5},{2,3},{6}

  • 求強連通分量的作用:把有向圖中具有相同性質的點找出來(求強連通分量),縮點,建立縮圖,能夠方便地進行其它操作

一.floyd算法

算法思想:如果任意兩個不同的頂點相互可達,則這兩點在同一強連通分量

  • 因為效率極低,不常使用,但理解極為簡單
//核心代碼
for(int k=1;k<=n;++k)//枚舉中間點 
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			if(g[i][k]&&g[k][j]) g[i][j]=1;
for(int i=1;i<=n;++i)//計算連通分量數 
{
	if(!vis[i]) vis[i]=++col;
	for(int j=1;j<=n;++j)
		if(g[i][j]==g[j][i]) vis[j]=vis[i];//同屬一個連通分量 
}

二.Tarjan算法

算法思想:
tarjan算法基於對圖深度優先搜索,用棧存儲整個強連通分量,將每一個強連通分量作為搜索樹上的一個子樹,而這個圖,就是一個完整的搜索樹。
DFN[ ]存儲點的搜索次序編號,LOW[ ]存儲強連通分量(對應搜索樹上的一個)子樹的根
判斷強連通分量:一個點出棧時 DFN==LOW

來看一個例子:

wOCr4K.md.png
wOCy9O.md.png
wOCgjH.md.png
wOCcge.md.png

銜接:搜索到1,LOW[4]變為1

wOCRud.md.png
wOCfHI.md.png

//核心代碼
void tarjan(int x)
{
	dfn[x]=low[x]=++tot;//新進點的初始化。
	stack[++index]=x;//進棧 
	visit[x]=1;//表示在棧里 
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(!dfn[v])//如果沒訪問過
		{
			darjan(v);//遞歸訪問 
			low[x]=min(low[x],low[v]);//比較誰是誰的父親/兒子,父親相當於強連通分量子樹最小根 
		}
		else if(visit[v]) low[x]=min(low[x],dfn[v]);//如果訪問過,並且還在棧里。 
	}
	if(low[x]==dfn[x])//是強連通分量子樹里的最小根
	{
		++num;//分量數加一 
		do{
			printf("%d ",stack[index]);
			visit[stack[index--]]=0;
		}while(x!=stack[index+1]);
		puts("");
	}
}

運用實例:消息的傳遞


三.Kosaraju算法

算法思想:
Kosaraju算法,基於兩次DFS
第一次DFS:對原圖進行深度優先遍歷,記錄每個頂點的離開時間。
第二次DFS:選擇具有最晚離開時間的頂點,對反向圖進行深度優先遍歷,並標記能夠遍歷到的頂點,這些頂點構成一個強連通分量

舉個栗子:

wOEKuF.png
wOEnjU.png

  • 深搜順序:

  • 對原圖進行深度優先遍歷后,頂點的離開時間分別為:
    1離開時間為7, 4離開時間為6,
    2離開時間為5, 3離開時間為4,
    5離開時間為3, 6離開時間為2,
    7離開時間為1。

  • 則按頂點按離開時間從大到小排列的序列為:1、4、2、3、5、6、7,

  • 按上述序列對反向圖進行深度優先遍歷,屬於同一次深度優先遍歷的頂點則屬於同一個強連

  • 結果:{1,2},{3},{4},{5,6,7}

//核心代碼
void dfs1(int x)
{
	vis[x]=1;
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(!vis[i]) dfs1(v);
	}
	stack[++index]=x;
}
void dfs2(int x)
{
	vis[x]=index;
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(!vis[v]) dfs2(i);
	}
}
int main()
{
	for(int i=1;i<=n;++i)
		if(!vis[i]) dfs1(i);
	memset(vis,0,sizeof(vis)); index=0;
	for(int i=1;i>=1;--i)//出棧,連通分量染色
		if(!vis[i]) ++index,dfs2(stack[i]);
}

四.總結與對比

Kosaraju算法的優勢:

  該算法依次求出的強連通分量已經符合拓撲排序.

Tarjan算法的優勢:

  相比Kosaraju算法擁有時間和空間上的巨大優勢.

使用范圍:

  一般圖中選用Tarjan算法,涉及求圖的拓撲性質時則選用Kosaraju算法


圖片來自百度文庫


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM