圖論演算法(6),圖論演算法
註:此演算法以有向圖作為輸入,並按照所在的強連通分量給出其頂點集的一個劃分。graph中的每個節點只在一個強連通分量裡出現,即使是單點。
任選一點開始進行深度優先搜尋(若dfs結束後仍有未訪問的節點,則再從中任選一點再從進行)。搜尋過程中已訪問的節點不再訪問。搜尋樹的若干子樹構成了圖的強連通分量。
節點按照被訪問的順序存入棧中。從搜尋樹的子樹返回至一個節點時,檢查該節點是否是某一連通分量的根節點,並將其從棧中刪除。如果某節點是強連通分量的根,則在它之前出棧且還不屬於其他強連通分量的節點構成了該節點所在的強連通分量。
註:根節點的性質
演算法的關鍵在於如何判定某節點是否是強連通分量的根。對於“強連通分量的根”這一說法僅針對此演算法。事實上,強連通分量沒有特定的“根”的。在這雷根節點指深度優先搜尋時強連通分量的節點構成了該節點所在的強連通分量。
為找到根結點,我們給每個結點v一個深度優先搜尋標號v.index,表示它是第幾個被訪問的結點。此外,每個結點v還有一個值v.lowlink,表示從v出發經有向邊可到達的所有結點中最小的index。顯然v.lowlink總是不大於v.index,且當從v出發經有向邊不能到達其他結點時,這兩個值相等。v.lowlink在深度優先搜尋的過程中求得,v是強連通分量的根若且唯若v.lowlink = v.index。
algorithm tarjan is input: 圖 G = (V, E) output: 以所在的強連通分量劃分的頂點集 index := 0 S := empty // 置棧為空白 for each v in V do if (v.index is undefined) strongconnect(v) end if function strongconnect(v) // 將未使用的最小index值作為結點v的index v.index := index v.lowlink := index index := index + 1 S.push(v) // 考慮v的後繼結點 for each (v, w) in E do if (w.index is undefined) then // 後繼結點w未訪問,遞迴調用 strongconnect(w) v.lowlink := min(v.lowlink, w.lowlink) else if (w is in S) then // w已在棧S中,亦即在當前強連通分量中 v.lowlink := min(v.lowlink, w.index) end if // 若v是根則出棧,並求得一個強連通分量 if (v.lowlink = v.index) then start a new strongly connected component repeat w := S.pop() add w to current strongly connected component until (w = v) output the current strongly connected component end if end function
變數index是深度優先搜尋的結點計數器。S是棧,初始為空白,用於儲存已經訪問但未被判定屬於任一強連通分量的結點。注意這並非一個一般深度優先搜尋的棧,結點不是在以它為根的子樹搜尋完成後出棧,而是在整個強連通分量被找到時。
最外層迴圈用於尋找未訪問的結點,以保證所有結點最終都會被訪問。strongconnect進行一次深度優先搜尋,並找到結點v的後繼結點構成的子圖中所有的強連通分量。
當一個結點完成遞迴時,若它的lowlink仍等於index,那麼它就是強連通分量的根。演算法將在此結點之後入棧(包含此結點)且仍在棧中的結點出棧,並作為一個強連通分量輸出。
備忘:
1.複雜度:對每個結點,過程strongconnect只被調用一次;整個程式中每條邊最多被考慮兩次。因此演算法的已耗用時間關於圖的邊數是線性,即O(|V|+|E|)。
2.判斷結點v'是否在棧中應在常數時間內完成,例如可以對每個結點儲存一個是否在棧中的標記。
3.同一個強連通分量內的結點是無序的,但此演算法具有如下性質:每個強連通分量都是在它的所有後繼強連通分量被求出之後求得的。因此,如果將同一強連通分量收縮為一個結點而構成一個有向非循環圖,這些強連通分量被求出的順序是這一新圖的拓撲序的逆序。
下面附上實現原始碼:
/* * Author : YANG Xiangyu * * Hint: tarjan scc * * The Chinese University of Hong Kong */package cyclefinder;import java.io.File;import java.io.FileNotFoundException;import java.io.PrintStream;import java.util.HashSet;import java.util.Scanner;import java.util.Set;import java.util.Stack;import java.util.Vector;public class TarjanSCC{class Node{//內部類定義節點Vector<Integer> adj;}Node[] graph;Stack<Integer> stack;int indices;boolean[] inStack;int[] index;int[] lowLink;int[] component;Set<Integer> nodeset;int numComponents;int n,m;public TarjanSCC(){graph=new Node[16000];inStack=new boolean[16000];index=new int[16000];lowLink=new int[16000];component=new int[16000];inStack=new boolean[16000];stack=new Stack<Integer>();nodeset=new HashSet<Integer>();for(int i=0;i<component.length;++i){component[i]=0xffffffff;}for(int i=0;i<graph.length;++i){graph[i]=new Node();graph[i].adj=new Vector<Integer>();}}public void strongconnect(int v){index[v]=indices;lowLink[v]=indices;indices++;stack.push(v);inStack[v]=true;for(int j=0;j<graph[v].adj.size();++j){int w=graph[v].adj.get(j);if(index[w]==0){strongconnect(w);lowLink[v]=Math.min(lowLink[v],lowLink[w]);}else if(inStack[w]){lowLink[v]=Math.min(lowLink[v],index[w]);}}if(lowLink[v]==index[v]){int w=0;do{w=stack.pop();component[w]=numComponents;inStack[w]=false;}while(v!=w&&!stack.empty());numComponents++;}}public void tarjanFunc(){indices=1;while(!stack.empty()){stack.pop();}Object[] v=nodeset.toArray();for(int i=0;i<v.length;++i){lowLink[(int) v[i]]=0;index[(int) v[i]]=0;inStack[(int) v[i]]=false;}numComponents=0;for(int i=0;i<v.length;++i){if(index[(int) v[i]]==0){strongconnect((int) v[i]);}}}public void input(String path) throws FileNotFoundException{Scanner input=new Scanner(new File(path));n=input.nextInt();m=input.nextInt();int a=0,b=0;for(int i=0;i<m;++i){a=input.nextInt();b=input.nextInt();graph[a].adj.add(b);nodeset.add(a);}input.close();}public void prints(String path) throws FileNotFoundException{PrintStream output = new PrintStream(path);for(int i=0;i<16000;++i){output.print(component[i]+" "+i+" ");for(int j=0;j<graph[i].adj.size();++j){output.print(graph[i].adj.get(j)+" ");}output.println();}System.out.println(numComponents);output.println(numComponents);output.close();}public static void main(String[] args) throws FileNotFoundException{TarjanSCC tarjanscc=new TarjanSCC();tarjanscc.input("C:/circleOrderedId.txt");tarjanscc.tarjanFunc();tarjanscc.prints("C:/tarjansccresults.txt");}}
解:圖論中常見的最短路徑演算法有幾種?都是什?
主要是有三種、、
第一種是最直接的貪心dijkstra演算法、、可以利用堆資料結構進行最佳化、、缺點就是不能求有負權的最短路與判斷負環、、
第二種是bellman-ford演算法、、根據鬆弛操作的性質是可以來判斷負環的、、時間複雜度是O(nm)的、、
第三種是SPFA演算法、、把他單獨拿出來作為一種演算法並不是非常好的、、他的實質應該是上面的bellman-ford演算法的隊列最佳化時間複雜度更低、O(KE)、K的值約等於2、、
圖論演算法示範平台,原始碼高分急
biyeshejiji@sina.cn 已經發到