對於無向圖html
算法1node
咱們知道對於環1-2-3-4-1,每一個節點的度都是2,基於此咱們有以下算法(這是相似於有向圖的拓撲排序):算法
時間複雜度爲O(E+V),其中E、V分別爲圖中邊和頂點的數目,這個算法咱們稍後分析算法3的時候再分析。數組
算法2函數
深度優先遍歷該圖,若是在遍歷的過程當中,發現某個節點有一條邊指向已經訪問過的節點,而且這個已訪問過的節點不是當前節點的父節點(這裏的父節點表示dfs遍歷順序中的父節點),則表示存在環。可是咱們不能僅僅使用一個bool數組來標誌節點是否訪問過。以下圖spa
從節點1開始遍歷-接着遍歷2-接着遍歷3,而後發現3有一條邊指向遍歷過的1,則存在環。可是回到1節點時,它的另外一條邊指向已訪問過的3,又把這個環重複計算了一次。htm
咱們按照算法導論22.3節深度優先搜索中,對每一個節點分爲三種狀態,白、灰、黑。開始時全部節點都是白色,當開始訪問某個節點時該節點變爲灰色,當該節點的全部鄰接點都訪問完,該節點顏色變爲黑色。那麼咱們的算法則爲:若是遍歷的過程當中發現某個節點有一條邊指向顏色爲灰的節點,那麼存在環。則在上面的例子中,回溯到1節點時,雖然有一條邊指向已經訪問過的3,可是3已是黑色,因此環不會被重複計算。blog
下面的代碼中visit數組的值分爲0 1 2三種狀態分別表明白色、灰色、黑色,調用函數dfs能夠輸出圖中存在的全部環,圖用鄰接矩陣表示,若是兩個節點之間沒有邊則對應的值爲INT_MAX排序
void dfsVisit(vector<vector<int> >&graph, int node, vector<int>&visit, vector<int>&father) { int n = graph.size(); visit[node] = 1; //cout<<node<<"-\n"; for(int i = 0; i < n; i++) if(i != node && graph[node][i] != INT_MAX) { if(visit[i] == 1 && i != father[node])//找到一個環 { int tmp = node; cout<<"cycle: "; while(tmp != i) { cout<<tmp<<"->"; tmp = father[tmp]; } cout<<tmp<<endl; } else if(visit[i] == 0) { father[i] = node; dfsVisit(graph, i, visit, father); } } visit[node] = 2; } void dfs(vector<vector<int> >&graph) { int n = graph.size(); vector<int> visit(n, 0); //visit按照算法導論22.3節分爲三種狀態 vector<int> father(n, -1);// father[i] 記錄遍歷過程當中i的父節點 for(int i = 0; i < n; i++) if(visit[i] == 0) dfsVisit(graph, i, visit, father); }
算法時間複雜度也是O(E+V)ip
對於有向圖
算法3
咱們都知道對於有向圖進行拓撲排序能夠判斷是否存在環。
對於有向圖的拓撲排序,你們都知道的kahn算法:
取出棧頂頂點a,輸出該頂點值,刪除該頂點
從圖中刪除全部以a爲起始點的邊,若是刪除的邊的另外一個頂點入度爲0,則把它入棧
若是利用上面的拓撲排序算法求環,能夠判斷是否有環,可是輸出環時有點麻煩。由於並非全部最後剩餘的點都是環中的頂點,好比以下狀況:
對這個圖運行上面的算法,最後全部的節點都不會被刪除,可是隻有1 2 3是環中的點,4不是環中的節點。
對於上面的算法1,和算法3的思想是同樣的,因此也會存在這個問題。
算法4
其實算法2能夠原封不動的搬來就能夠檢測而且輸出全部有向圖中的環 本文地址
算法5
根據有向圖的強連通份量算法,每一個強連通份量中一定存在環,由於根據強連通份量的定義:從頂點 i 到 j 有一條路徑,而且從 j 到 i 也有一條路徑。求強連通份量的算法能夠參考維基百科here
補充:利用dfs來拓撲排序
只要對算法2稍微改動就能夠輸出有向圖的拓撲排序結果,即按照節點標記爲黑色的時間,越先標記爲黑色,在拓撲序列中越靠後。咱們在算法2的基礎上加了一個棧來保存拓撲排序的結果,只有dfsVisit的最後一行有改動,該算法,能夠完成拓撲排序,而且同時能夠檢測圖中是否有環。(該算法思想和算法導論22.4節拓撲排序同樣)
stack<int> tuopu; void dfsVisit(vector<vector<int> >&graph, int node, vector<int>&visit, vector<int>&father) { int n = graph.size(); visit[node] = 1; //cout<<node<<"-\n"; for(int i = 0; i < n; i++) if(i != node && graph[node][i] != INT_MAX) { if(visit[i] == 1 && i != father[node])//找到一個環 { int tmp = node; cout<<"cycle: "; while(tmp != i) { cout<<tmp<<"->"; tmp = father[tmp]; } cout<<tmp<<endl; } else if(visit[i] == 0) { father[i] = node; dfsVisit(graph, i, visit, father); } } visit[node] = 2; tuopu.push(node); } void dfs(vector<vector<int> >&graph) { int n = graph.size(); vector<int> visit(n, 0); //visit按照算法導論22.3節分爲三種狀態 vector<int> father(n, -1);// father[i] 記錄遍歷過程當中i的父節點 for(int i = 0; i < n; i++) if(visit[i] == 0) dfsVisit(graph, i, visit, father); }
【版權聲明】轉載請註明出處:http://www.cnblogs.com/TenosDoIt/p/3644225.html