1. 程式人生 > >Kosaraju演算法查詢有向圖的強連通分支

Kosaraju演算法查詢有向圖的強連通分支

給定一個有向圖 G = (V, E),對於任意一對頂點 u 和 v,有 u --> v 和 v --> u,亦即,頂點 u 和 v 是互相可達的,則說明該圖 G 是強連通的(Strongly Connected)。如下圖中,任意兩個頂點都是互相可達的。

對於無向圖,判斷圖是否是強連通的,可以直接使用深度優先搜尋(DFS)或廣度優先搜尋(BFS),從任意一個頂點出發,如果遍歷的結果包含所有的頂點,則說明圖是強連通的。

而對於有向圖,則不能使用 DFS 或 BFS 進行直接遍歷來判斷。如下圖中,如果從頂點 0 開始遍歷則可判斷是強連通的,而如果從其它頂點開始遍歷則無法抵達所有節點。

那麼,該如何判斷有向圖的強連通性呢?

實際上,解決該問題的較好的方式就是使用 強連通分支演算法(SCC:Strongly Connected Components) ,可以在  O(V+E) 時間內找到所有的 SCC。如果 SCC 的數量是 1,則說明整個圖是強連通的。

有向圖 G = (V, E) 的一個強連通分支是一個最大的頂點集合 C,C 是 V 的子集,對於 C 中的每一對頂點 u 和 v,有 u --> v 和 v --> u,亦即,頂點 u 和 v 是互相可達的。

實現 SCC 的一種演算法就是 Kosaraju 演算法 。Kosaraju 演算法基於深度優先搜尋(DFS),並對圖進行兩次 DFS 遍歷,演算法步驟如下:

  1. 初始化設定所有的頂點為未訪問的;
  2. 從任意頂點 v 開始進行 DFS 遍歷,如果遍歷結果沒有訪問到所有頂點,則說明圖不是強連通的;
  3. 置換整個圖(Reverse Graph);
  4. 設定置換後的圖中的所有頂點為未訪問過的;
  5. 從與步驟 2 中相同的頂點 v 開始做 DFS 遍歷,如果遍歷沒有訪問到所有頂點,則說明圖不是強連通的,否則說明圖是強連通的。

Kosaraju 演算法的思想就是,如果從頂點 v 可以抵達所有頂點,並且所有頂點都可以抵達 v,則說明圖是強連通的。

2. java原始碼

/**找到有向圖的強連通分支,正向遍歷,按照後根序壓棧,根據反向圖檢視結點是否可達*/
import java.util.*;
public class Kosaraju {
	
   static class Graph
   {
	   int n;
	   List<Integer>[] adj;
	   Graph(int n)
	   {
		   this.n=n;
		   this.adj=new List[n];
		   for(int i=0;i<n;i++)
		   {
			   this.adj[i]=new ArrayList<Integer>();
		   }
	   }
	   
	   public void addEdge(int v,int w)
	   {
		   this.adj[v].add(w);
	   }
	   
	   /**正向遍歷,以後根序壓棧,保證根先出棧*/
	   public void fillorder(int v,boolean[] visited,Stack<Integer> s)
	   {
		   visited[v]=true;
		   for(Integer i:this.adj[v])
		   {
			   if(!visited[i])
			   {
				   fillorder(i,visited,s);
			   }
		   }
		   s.push(v);
	   }
	   /**得到反向圖*/
	   public Graph getTranspose()
	   {
		  Graph gv=new Graph(this.n);
		  for(int i=0;i<n;i++)
		  {
			  for(Integer j:this.adj[i])
			  {
				  gv.adj[j].add(i);
			  }
		  }
		  return gv;
	   }
	   
	   /**DFS列印連通分支*/
	   public void DFSUtil(int v,boolean[] visited)
	   {
		   visited[v]=true;
		   System.out.print(v+" ");
		   for(Integer i:adj[v])
		   {
			   if(!visited[i])
			   {
				   DFSUtil(i,visited);
			   }
		   }
	   }
	   
	   /**按照Kosaraju演算法的步驟執行*/
	   public void printSCCs()
	   {
		   Stack<Integer> s=new Stack<Integer>();
		   boolean[] visited=new boolean[this.n];
		   for(int i=0;i<n;i++)
		   {
			   visited[i]=false;
		   }
		   /**後根序壓棧*/
		   for(int i=0;i<n;i++)
		   {
			   if(!visited[i])
			   {
				   fillorder(i,visited,s);
			   }
		   }
		   /**得到反向圖*/
		   Graph gr=this.getTranspose();
		   for(int i=0;i<n;i++)
		   {
			   visited[i]=false;
		   }
		   /**依據反向圖算可達性*/
		   while(!s.empty())
		   {
			   int v=s.pop();
			   if(visited[v]==false)
			   {
				   gr.DFSUtil(v, visited);
				   System.out.println();
			   }
			   
		   }
	   }
   }
   
   public static void main(String args[])
   {
	   Graph g=new Graph(5);
	   g.addEdge(1, 0);
	   g.addEdge(0, 2);
	   g.addEdge(2, 1);
	   g.addEdge(3, 0);
	   g.addEdge(3, 4);
	   g.printSCCs();
   }
   
  
}

3. 正確性證明

1. 第一次DFS有向圖G時,最後記錄下的節點必為最後一棵生成樹的根節點。 
證明:假設最後記錄下節點不是樹根,則必存在一節點為樹根,且樹根節點必為此節點祖先;而由後根序訪問可知祖先節點比此節點更晚訪問,矛盾;原命題成立

2. 第一次DFS的生成森林中,取兩節點A、B,滿足:B比A更晚記錄下,且B不是A的祖先(即在第一次DFS中,A、B處於不同的生成樹中);則在第二次DFS的生成森林中,B不是A的祖先,且A也不是B的祖先(即在第二次DFS中,A、B處於不同的生成樹中)。 
證明:假設在第二次DFS的生成森林中,B是A的祖先,則反圖GT中存在B到A路徑,即第一次DFS生成森林中,A是B的祖先,則A必比B更晚記錄下,矛盾;假設在第二次DFS的生成森林中,A是B的祖先,則反圖GT中存在A到B路徑,即第一次DFS生成森林中,B是A的祖先,矛盾;原命題成立

3. 按上述步驟求出的必為強連通分量 
證明:首先,證明2保證了第二次DFS中的每一棵樹都是第一次DFS中的某棵樹或某棵樹的子樹。其次,對於第二次DFS中的每棵樹,第一次DFS保證了從根到其子孫的連通性,第二次DFS保證了根到子孫的反向連通性(即子孫到根的連通性);由此,此樹中的每個節點都通過其根相互連通。

4. 演算法複雜度

相關推薦

Kosaraju演算法查詢圖的強連通分支

給定一個有向圖 G = (V, E),對於任意一對頂點 u 和 v,有 u --> v 和 v --> u,亦即,頂點 u 和 v 是互相可達的,則說明該圖 G 是強連通的(Strongly Connected)。如下圖中,任意兩個頂點都是互相可達的。 對於無向圖,判斷圖是否是強連通的,

夜深人靜寫演算法(十)- 圖強連通和2-sat問題

一、引例   1、同學會   【例題1】作者有N個同學,並且N個同學中有M對關係,M對關係描述為(a,b)代表a有b的電話號碼(不代表b有a的)。現在作者想舉辦一次同

『圖論』圖強連通分量的Tarjan演算法

在圖論中,一個有向圖被成為是強連通的(strongly connected)當且僅當每一對不相同結點u和v間既存在從u到v的路徑也存在從v到u的路徑。有向圖的極大強連通子圖(這裡指點數極大)被稱為強連通分量(strongly connected component)。 比如說這個有向圖中,點\(1,2,

圖強連通分量的Tarjian演算法

[有向圖強連通分量] 在有向圖G中,如果兩個頂點間至少存在一條路徑,稱兩個頂點強連通(strongly connected)。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。非強連通圖有向圖的極大強連通子圖,稱為強連通分量(strongly connected

圖強連通分量的Tarjan算法

雙向 強連通分量 地址 nbsp 指向 代碼 堆棧 全部 blank 原文地址:https://www.byvoid.com/blog/scc-tarjan/ [有向圖強連通分量] 在有向圖G中,如果兩個頂點間至少存在一條路徑,稱兩個頂點強連通(strongly

筆記:Tarjan算法 求解圖強連通分量的線性時間的算法

true fff lan number lock 無環 還需 sin 第一次 Tarjan他爾賤算法 求解有向圖強連通分量的線性時間的算法 百度百科 https://baike.baidu.com/item/tarjan%E7%AE%97%E6%B3%95/10687825

hdu1269 圖強連通 【Targan】(模板)

== color truct 相同 ext 結束 數據 訓練 算法 <題目鏈接> 題目大意: 為了訓練小希的方向感,Gardon建立了一座大城堡,裏面有N個房間(N<=10000)和M條通道(M<=100000),每個通道都是單向的,就是說若稱某通道

對求圖強連通分量的tarjan算法原理的一點理解

深度優先 含義 出現 組合 分支 ron 滿足 根節點 節點和 先簡單敘述一下tarjan算法的執行過程(其他諸如偽代碼之類的相關細節可以自己網上搜索,這裏就不重復貼出了): 用到兩類數組: dfs[]:DFS過程中給定節點的深度優先數,即該節點在DFS中被訪問的次序 lo

HDU3861-The King’s Problem(圖強連通縮點+最小路徑覆蓋)

題目連結 題意:題目大意:一個有向圖,讓你按規則劃分區域,要求劃分的區域數最少。  規則如下:  1、有邊u到v以及有邊v到u,則u,v必須劃分到同一個區域內。  2、一個區域內的兩點至少要有一方能到達另一方。  3、一個點只能劃分到一個區域內。 思路:根據規則1可

CCF——高速公路(連通分量)

題目: 問題描述   某國有n個城市,為了使得城市間的交通更便利,該國國王打算在城市之間修一些高速公路,由於經費限制,國王打算第一階段先在部分城市之間修一些單向的高速公路。   現在,大臣們幫國王擬了一個修高速公路的計劃。看了計劃後,國王發現,有些城市之間可以通過高速

【資料結構與演算法圖的最短路徑實現

Goal: Practice the algorithms of shortest pathproblem. Task:用一個有向圖表示給定的n個(要求至少10個)城市(或校園中的一些地點)及其之間的道路、距離情況,道路是有方向的。要求完成功能:根據使用者輸入的任意兩個城

演算法無環圖(DAG)的拓撲排序

更新: 拓撲排序有2中方法(最後結果可能不同,因為拓撲排序有多解)。 一個簡單的求拓撲排序的演算法是先找出任意一個沒有入邊的頂點,然後將它和它的邊從圖中刪除。然後對剩餘部分使用同樣的操作。 public ArrayList<Integer&g

最短路徑——迪傑斯坷垃演算法圖、單源最短路徑)

最短路徑的演算法有兩種:迪傑斯坷垃演算法和弗洛伊德演算法。 但是兩種演算法各有優劣: 迪傑斯坷垃演算法適合單源點最短路徑的獲取, 弗洛伊德演算法適合各點間最短路徑的獲取,即多源點最短路徑的獲取; 今天主要講解迪傑斯坷垃演算法。 一、演算法步驟: 1、獲取鄰接矩陣,確定起始點

Floyd-Warshall演算法圖)

#include<stdio.h> #include<malloc.h> #include<string.h> #include<stack> usin

HDU4612-Warm up(無圖強連通分量縮點)

Warm up                                                                            Time Limit: 10000/5000 MS (Java/Others)    Memory Li

Kosaraju 演算法求解一個圖的強連通分支個數

基本介紹 網上看了很多關於求解一個有向圖的強連通分支個數的演算法,其中最著名的莫過於: Kosaraju 演算法 看的比較暈! 過程如下: 1。 建立一個空的棧 S,並做一次 DFS 遍歷。在 DFS 遍歷中,當在遞迴呼叫 DSF 訪問鄰接頂點時,將

圖的強連通分支演算法kosaraju(C語言實現)

/* Kosaraju求強連通分量鄰接矩陣 */#include "stdio.h"#include "stdlib.h"#define Vexnum 100int map1[Vexnum][Vexnum]={0};int nmap[Vexnum][Vexnum]={0};int visited[Vexnum

ACM圖論__圖的強連通分支演算法

求有向圖的強連通分支,主要有兩種演算法tarjan演算法和kosaraju演算法,這裡介紹tarjan演算法先來看幾個定義:(1)連通:兩個點可以相互到達(2)強連通(strongly connected): 在一個有向圖G裡,設兩個點 a b 發現,由a有一條路可以走到b,

圖的強連通分支(Tarjan演算法)

強連通分支 如果兩個頂點可以相互通達,則稱兩個頂點強連通(strongly connected)。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。非強連通圖有向圖的極大強連通子圖,稱為強連通分量(strongly connected components

圖的強連通分解--Kosaraju演算法

/** * 圖的強連通分解——Kosaraju演算法 **/ class Kosaraju { public: Kosaraju(vector<vector<int> &g