在一些經典算法中,經常需要判斷一些圖是否具有環路,比如拓撲排序,需要在最初判斷該圖是否有環路,如有有環路,則無法找到最長的一條線,比如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
實驗結果:

