在一些經典算法中,經常需要判斷一些圖是否具有環路,比如拓撲排序,需要在最初判斷該圖是否有環路,如有有環路,則無法找到最長的一條線,比如dijkstra算法,每找到一條最短的邊,都要判斷找到的邊和現有的樹是否已經構成了環路。
因此,在這篇博客,我們重點來說一個判斷圖是否有環的算法。
首先我們介紹一個對於無向圖和有向圖通用的算法,先講算法思路:
1.統計各個圖中各個點的入度數(能夠到達這個點的點的數量)。
2.然后找出入度數為0的點(無向圖找入度數為1的點)。
3.刪除入度數為0的點,將其邊也刪除。
4.重復2,直到所有點入度都為0,則為無環圖,如果找不到入度為0的點,則為有環圖。
該算法的精髓在於對於一個環路(以有向圖為例),1->2,2->3,3->1,你會發現找不到一個入度為0的點,因此這個方法是可行的。
對於無向圖和有向圖來說,這個算法是通用的。在這我只寫了對於有向圖的判斷的算法,具體的實現代碼如下:
#include<stdio.h> using namespace std; int graph[100][100];//用來存儲圖的數組 bool isVisited[100];//判斷這個點是否已經刪除 int main() { int n,e; while (scanf("%d",&n)!=EOF&&n!=0)//獲取點數 { for(int i = 0;i<100;i++) { isVisited[i] = false; for(int j = 0 ;j<100;j++) { graph[i][j] = -1;//初始化數據,所有的邊都為-1,代表這兩個點之間不能聯通 } } scanf("%d",&e);//獲取邊數 for(int i = 0 ;i<e;i++)//構建圖 { int a,b,c; scanf("%d %d %d",&a,&b,&c); graph[a-1][b-1] = c; } int isResult = true; for(int i = 0 ;i<n;i++)//進行n次循環,每次循環刪除一個入度為0的點,所以進行n次循環 { for(int j = 0;j<n;j++)//遍歷所有的點,找入度為0的點 { if(!isVisited[j])//判斷該點是否刪除 { bool isCanVisited = true;//輔助變量,判斷這個點是否入度為0 for(int k = 0;k<n ;k++) { if(graph[k][j]!=-1) { isCanVisited = false;//如果存在能夠訪問這個點的邊,則該點入度不為0 } } if(isCanVisited)//如果該點入度為0,則下邊是刪除該點和刪除其相鄰邊 { for(int k = 0 ;k<n;k++) { graph[j][k] = -1;//刪除相鄰邊,即將值變為-1 } isVisited[j] = true;//刪除該點 } } } isResult = true; for(int j = 0 ;j<n;j++)//進行循環判斷當前多有點是否已經全部刪除,如果全部刪除,如果全部刪除則跳出,否則繼續循環 { if(!isVisited[j]) { isResult = false; } } if(isResult) break; } isResult = true; for(int i = 0 ;i<n;i++)//在所有點遍歷后,則通過這個循環來判斷是否所有點都已經刪除,如果全部刪除,則為無環圖,否則為有環圖 { if(!isVisited[i]) isResult = false; } if(isResult) printf("無環"); if(!isResult) printf("有環"); } return 0; }
實驗數據(第一行輸入n,e,n代表的是點數,e代表的是邊數,接下來e行代表具體的邊和其權值(權值暫時不用理會,是后續拓撲排序所有,因此當前暫時都為1)):
5 4
1 2 1
1 3 1
2 3 1
4 5 1
5 4
1 2 1
2 1 1
2 3 1
4 5 1
實驗結果: