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 遍歷,演算法步驟如下:
- 初始化設定所有的頂點為未訪問的;
- 從任意頂點 v 開始進行 DFS 遍歷,如果遍歷結果沒有訪問到所有頂點,則說明圖不是強連通的;
- 置換整個圖(Reverse Graph);
- 設定置換後的圖中的所有頂點為未訪問過的;
- 從與步驟 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