關於無向圖判斷是否存在迴路的方法
問題:給出一個演算法,用它來確定一個給定的無向圖G=(V,E)中是否包含一個迴路。所給出的演算法的執行時間為O(V),這一時間獨立於|E|
解答:我們都知道對於一個無向圖而言,如果它能表示成一棵樹,那麼它一定沒有迴路,並且有|E|=|V|-1,如果在這個樹上新增一條邊,那麼就構成了迴路,如果在這個樹中去掉一個邊就成了森林(注意:如果只是限定|E|<|V|-1它不一定是森林,它當中可能存在無向連通子圖)。 對於這個題目我們可以用DFS來做,每當訪問當前節點的鄰接表時,如果存在某個鄰接的元素已被標記為訪問狀態,那麼這個圖就是存在迴路的。總的時間代價是O(|E|+|V|),因為E<=|V|-1(如果|E|>|V|-1根據無向圖的性質,那麼這個無向圖一定存在迴路),所以O(|E|+|V|)=O(|V|)
來自:http://blog.163.com/kevinlee_2010/
其他思路:
第一種是類似有向圖拓撲排序的思路:(參考有向圖判斷迴路的解答)
如果存在迴路,則必存在一個子圖,是一個環。因此該子圖中所有頂點入度>=1。
演算法: 在有向圖中,先找出入度為0的頂點,刪除與這個頂點相關聯的邊(出邊),將與這些邊相關的其它頂點的入度減1,迴圈直到沒有入度為0的定點。如果此時還有未被刪除頂點,則必存在環路,否則不存在環路。
無向圖則可以轉化為: 如果存在迴路,則必存在一個子圖,是一個環路。因此環路中所有頂點的度>=2。
演算法:
第一步:刪除所有度<=1的頂點及相關的邊,並將另外與這些邊相關的其它頂點的度減一。
第二步:將度數變為1的頂點排入佇列,並從該佇列中取出一個頂點重複步驟一。
如果最後還有未刪除頂點,則存在環,否則沒有環。
演算法分析:
由於有m條邊,n個頂點。如果m>=n,則根據圖論知識可直接判斷存在環路。
(證明:如果沒有環路,則該圖必然是k棵樹 k>=1。根據樹的性質,邊的數目m = n-k。
k>=1,所以:m<n)
如果m<n 則按照上面的演算法每刪除一個度為0的頂點操作一次(最多n次),或每刪除一個度為1的頂點(同時刪一條邊)操作一次(最多m次)。這兩種操作的總數不會超過m+n。由於m<n,所以演算法複雜度為O(n)
第二種為藉助廣度優先的策略:
任選一個頂點進行廣度優先搜尋。由於廣度優先的演算法本質就是從一個頂點出發將圖按距離該頂點的遠近層層展開為樹形結構,如果存在某個頂點被訪問兩次表明樹形展開層次結構中存在回邊,因此則必存在迴路。
一種比較直接的思路是:為了要判斷是否存在環路,需要在每個新近被加入訪問佇列的頂點上記錄下該頂點的上一步的父頂點是哪一個,除了父頂點外其它相關頂點可以加入廣度優先演算法的佇列,並將所有入隊頂點的訪問次數加一。如果發現訪問次數>1的情況,則存在迴路。反之,如果演算法結束沒有訪問次數>1的情況則不存在迴路。
賀司衡同學借鑑MIT教程《Introduction to Algorithms》中廣度優先周遊圖的處理方法:
對每一個元素染色(實際操作可用整數表示)。建立一個佇列Q。初始狀態將全部元素染成白色。從圖中第一個節點(頂點陣列中第一個元素),開始廣度周遊。元素入隊染成灰色,出隊後染成黑色。例如元素A入隊,染成灰色,A出隊,染成黑色,鄰近節點有三種可能:黑、白、灰。若為黑色節點是深度周遊序列之前已訪問過的,忽略不管(因而避免了“回訪”現象):若是白色節點則入隊,染灰;若有灰色節點表明該節點以存在於佇列中,即兩個深度優先周遊方向的序列將在此匯合,也就說明無向圖存在迴路。上述三種情況中,有灰色情況則表明有迴路,若沒有則繼續迴圈直至遍歷每個節點,說明沒有迴路。
另外一種思路是:
對每一個連通子圖,以任何一點作為樹的根,進行一遍廣搜。記節點i的深度為deep(i),如果一個點u除父節點v外,還存在與之連通的點w,滿足deep(w)≤deep(u),既存在回邊,則存在環,時間複雜度o(n)。
第三種方法
利用圖論的知識,即如果存在迴路,則至少存在一個連通分量中邊的數目m >= 頂點數n。因此,在圖的廣度或深度周遊演算法中記錄每個連通分量中的頂點數n’,O(n);統計與這些頂點相關聯的邊的數目m’如果m’ = n’,則演算法結束,存在迴路,O(n)。否則不存在迴路。