1. 程式人生 > >有向圖、無向圖是否有環的判斷

有向圖、無向圖是否有環的判斷


今天在做資料庫的排程衝突可序列性判別的程式,中間要用到有向圖中環判定的問題,特摘錄如下。這些演算法和思想都是來自網上的,在此感謝原作者!

先介紹一下無向圖的判斷演算法,這個比較簡單:

判斷無向圖中是否存在迴路(環)的演算法描述

如果存在迴路,則必存在一個子圖,是一個環路。環路中所有頂點的度>=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)

接下來介紹有向圖是否有環的判定演算法,主要有深度優先和拓撲排序2中方法。

 1、拓撲排序,如果能夠用拓撲排序完成對圖中所有節點的排序的話,就說明這個圖中沒有環,而如果不能完成,則說明有環。

    2、可以用Strongly Connected Components來做,我們可以回憶一下強連通子圖的概念,就是說對於一個圖的某個子圖,該子圖中的任意u->v,必有v->u,則這是一個強連通子圖。這個限定正好是環的概念。所以我想,通過尋找圖的強連通子圖的方法應該可以找出一個圖中到底有沒有環、有幾個環。

    3、就是用一個改進的DFS

    剛看到這個問題的時候,我想單純用DFS就可以解決問題了。但細想一下,是不能夠的。如果題目給出的是一個無向圖,那麼OK,DFS是可以解決的。但無向圖得不出正確結果的。比如:A->B,A->C->B,我們用DFS來處理這個圖,我們會得出它有環,但其實沒有。

    我們可以對DFS稍加變化,來解決這個問題。解決的方法如下:

    圖中的一個節點,根據其C[N]的值,有三種狀態:

    0,此節點沒有被訪問過

    -1,被訪問過至少1次,其後代節點正在被訪問中

    1,其後代節點都被訪問過。

    按照這樣的假設,當按照DFS進行搜尋時,碰到一個節點時有三種可能:

    1、如果C[V]=0,這是一個新的節點,不做處理

    2、如果C[V]=-1,說明是在訪問該節點的後代的過程中訪問到該節點本身,則圖中有環。

    3、如果C[V]=1,類似於2的推導,沒有環。    在程式中加上一些特殊的處理,即可以找出圖中有幾個環,並記錄每個環的路徑

上面這個演算法我沒看懂。。所以沒實現,但是自己用DFS實現了環檢測。

		// DFS,發現迴路(返回true)則不可序列化,返回false
		for (int i = 1; i <= n; i++) {
			if (dfsCheckCircuit(i))
				return false;
		}

	// 如果發現迴路則返回true,否則遍歷結束返回false
	private boolean dfsCheckCircuit(int current) {
		if (walked[current]) {
			return true;
		}
		walked[current] = true;
		for (int i = 1; i <= n; i++)
			if (digraph[current][i]) {
				if (dfsCheckCircuit(i)) {
					return true;
				}
			}
		walked[current] = false;
		return false;
	}