沒有找到原文出處,請參考一下連結:
http://www.cnblogs.com/hiside/archive/2010/12/01/1893878.html
http://topic.csdn.net/u/20071023/11/3edb81fc-37b2-4506-906e-44dc0fc521f2.html
一、無向圖:方法1:
如果存在迴路,則必存在一個子圖,是一個環路。環路中所有頂點的度>=2。
n演算法: 第一步:刪除所有度<=1的頂點及相關的邊,並將另外與這些邊相關的其它頂點的度減一。
第二步:將度數變為1的頂點排入隊列,並從該隊列中取出一個頂點重複步驟一。
如果最後還有未刪除頂點,則存在環,否則沒有環。
n演算法分析: 由於有m條邊,n個頂點。
i)如果m>=n,則根據圖論知識可直接判斷存在環路。(證明:如果沒有環路,則該圖必然是k棵樹 k>=1。根據樹的性質,邊的數目m = n-k。k>=1,所以:m<n)
ii)如果m<n 則按照上面的演算法每刪除一個度為0的頂點操作一次(最多n次),或每刪除一個度為1的頂點(同時刪一條邊)操作一次(最多m次)。這兩種操作的總數不會超過m+n。由於m<n,所以演算法複雜度為O(n)。
註: 該方法,演算法複雜度不止O(V),首先初始時刻統計所有頂點的度的時候,複雜度為(V + E),即使在後來的迴圈中E>=V,這樣演算法的複雜度也只能為O(V + E)。其次,在每次迴圈時,刪除度為1的頂點,那麼就必須將與這個頂點相連的點的度減一,並且執行delete node from list[list[node]],這裡尋找的複雜度為list[list[node]]的長度,只有這樣才能保證當degree[i]=1時,list[i]裡面只有一個點。這樣最差的複雜度就為O(EV)了。
方法2:DFS搜尋圖,圖中的邊只可能是樹邊或反向邊,一旦發現反向邊,則表明存在環。該演算法的複雜度為O(V)。
方法3:摘自:http://blog.csdn.net/lzrzhao/archive/2008/03/13/2175787.aspx
PS:此方法於2011-6-12補充
假定:圖頂點個數為M,邊條數為E
遍曆一遍,判斷圖分為幾部分(假定為P部分,即圖有 P 個連通分量)對於每一個連通分量,如果無環則只能是樹,即:邊數=結點數-1隻要有一個滿足 邊數 > 結點數-1
原圖就有環
將P個連通分量的不等式相加,就得到:P1:E1=M1-1P2:E2=M2-1...PN:EN>MN-1 所有邊數(E) > 所有結點數(M) - 連通分量個數(P)
即: E + P > M 所以只要判斷結果 E + P > M 就表示原圖有環,否則無環.執行個體代碼如下:
#include<iostream>#include<malloc.h>using namespace std;#define maxNum 100 //定義鄰接舉證的最大定點數int visited[maxNum];//通過visited數組來標記這個頂點是否被訪問過,0表示未被訪問,1表示被訪問int DFS_Count;//連通組件個數,用於測試無向圖是否連通,DFS_Count=1表示只有一個連通組件,所以整個無向圖是連通的int pre[maxNum];int post[maxNum];int point;//pre和post的值//圖的鄰接矩陣表示結構typedef struct{char v[maxNum];//圖的頂點資訊int e[maxNum][maxNum];//圖的頂點資訊int vNum;//頂點個數int eNum;//邊的個數}graph;void createGraph(graph *g);//建立圖gvoid DFS(graph *g);//深度優先遍曆圖gvoid dfs(graph *g,int i);//從頂點i開始深度優先遍曆與其相鄰的點void dfs(graph *g,int i){//cout<<"頂點"<<g->v[i]<<"已經被訪問"<<endl;cout<<"頂點"<<i<<"已經被訪問"<<endl;visited[i]=1;//標記頂點i被訪問pre[i]=++point;for(int j=1;j<=g->vNum;j++){if(g->e[i][j]!=0&&visited[j]==0)dfs(g,j);}post[i]=++point;}void DFS(graph *g){int i;//初始化visited數組,表示一開始所有頂點都未被訪問過for(i=1;i<=g->vNum;i++){visited[i]=0;pre[i]=0;post[i]=0;}//初始化pre和postpoint=0;//初始化連通組件數為0DFS_Count=0;//深度優先搜尋for(i=1;i<=g->vNum;i++){if(visited[i]==0)//如果這個頂點為被訪問過,則從i頂點出發進行深度優先遍曆{DFS_Count++;//統計調用void dfs(graph *g,int i);的次數dfs(g,i);}}}void createGraph(graph *g)//建立圖g{cout<<"正在建立無向圖..."<<endl;cout<<"請輸入頂點個數vNum:";cin>>g->vNum;cout<<"請輸入邊的個數eNum:";cin>>g->eNum;int i,j;//輸入頂點資訊//cout<<"請輸入頂點資訊:"<<endl;//for(i=0;i<g->vNum;i++)//cin>>g->v[i];//初始畫圖gfor(i=1;i<=g->vNum;i++)for(j=1;j<=g->vNum;j++)g->e[i][j]=0;//輸入邊的情況cout<<"請輸入邊的頭和尾"<<endl;for(int k=0;k<g->eNum;k++){cin>>i>>j;g->e[i][j]=1;g->e[j][i]=1;//無向圖對稱}}int main(){graph *g;g=(graph*)malloc(sizeof(graph));createGraph(g);//建立圖gDFS(g);//深度優先遍曆//連通組件數,用於判斷是否連通圖cout<<"連通組件數量:";cout<<DFS_Count<<endl;if(DFS_Count==1)cout<<"圖g是連通圖"<<endl;else if(DFS_Count>1)cout<<"圖g不是連通圖"<<endl;//各頂點的pre和post值for(int i=1;i<=g->vNum;i++)cout<<"頂點"<<i<<"的pre和post分別為:"<<pre[i]<<" "<<post[i]<<endl;//cout<<endl;//判斷無向圖中是否有環if(g->eNum+DFS_Count>g->vNum)cout<<"圖g中存在環"<<endl;elsecout<<"圖g中不存在環"<<endl;int k;cin>>k;return 0;}/*輸入:正在建立無向圖...請輸入頂點個數vNum:10請輸入邊的個數eNum:9請輸入邊的頭和尾1 21 42 52 64 75 96 37 89 10*/注意:有向圖不能使用此方法。比如1->2,1-3,2->3,4->5,如果使用上述方法會判定為含有還,但並非如此
有向圖:
主要有深度優先和拓撲排序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的推導,沒有環。 在程式中加上一些特殊的處理,即可以找出圖中有幾個環,並記錄每個環的路徑
PS:此代碼實現於2011-6-13補充
改進DFS演算法程式碼範例(判斷是否是一個有向非循環圖)
#include<iostream>#include<malloc.h>using namespace std;#define maxNum 100 //定義鄰接舉證的最大定點數int pre[maxNum];int post[maxNum];int point=0;//pre和post的值bool is_DAG=true;//標識位,表示有向非循環圖/*頂點顏色表 color[u] 0 白色,未被訪問過的節點標白色 -1 灰色,已經被訪問過一次的節點標灰色 1 黑色,當該節點的所有後代都被訪問過標黑色反向邊: 如果第一次訪問(u,v)時v為灰色,則(u,v)為反向邊。在對圖的深度優先搜尋中沒有發現 反向邊,則該圖沒有迴路程式判斷依據:仍然是按圖的節點深度遍曆,訪問到V時,V若被訪問過,那麼有2種狀態:color[u]=-1,程式跳出,存在環color[u]=1,程式繼續,這不是環時間複雜度:O(n+e)*/int color[maxNum];//頂點顏色表 color[u]//圖的鄰接矩陣表示結構typedef struct{char v[maxNum];//圖的頂點資訊int e[maxNum][maxNum];//圖的頂點資訊int vNum;//頂點個數int eNum;//邊的個數}graph;void createGraph(graph *g);//建立圖gvoid DFS(graph *g);//深度優先遍曆圖gvoid dfs(graph *g,int i);//從頂點i開始深度優先遍曆與其相鄰的點void dfs(graph *g,int i){//cout<<"頂點"<<g->v[i]<<"已經被訪問"<<endl;cout<<"頂點"<<i<<"已經被訪問"<<endl;color[i]=-1;pre[i]=++point;for(int j=1;j<=g->vNum;j++){if(g->e[i][j]!=0){if(color[j]==-1)//探索到回邊,存在環{is_DAG=false;//不是有向非循環圖}else if(color[j]==0)dfs(g,j);}}post[i]=++point;color[i]=1;//表示i的後裔節點都被訪問過}void DFS(graph *g){int i;//初始化color數組,表示一開始所有頂點都未被訪問過,//初始化pre和postfor(i=1;i<=g->vNum;i++){color[i]=0;pre[i]=0;post[i]=0;}//深度優先搜尋for(i=1;i<=g->vNum;i++){if(color[i]==0)//如果這個頂點為被訪問過,則從i頂點出發進行深度優先遍曆{dfs(g,i);}}}void createGraph(graph *g)//建立圖g{cout<<"正在建立無向圖..."<<endl;cout<<"請輸入頂點個數vNum:";cin>>g->vNum;cout<<"請輸入邊的個數eNum:";cin>>g->eNum;int i,j;//初始畫圖gfor(i=1;i<=g->vNum;i++)for(j=1;j<=g->vNum;j++)g->e[i][j]=0;//輸入邊的情況cout<<"請輸入邊的頭和尾"<<endl;for(int k=1;k<=g->eNum;k++){cin>>i>>j;g->e[i][j]=1;}}int main(){graph *g;g=(graph*)malloc(sizeof(graph));createGraph(g);//建立圖gDFS(g);//深度優先遍曆//各頂點的pre和post值for(int i=1;i<=g->vNum;i++)cout<<"頂點"<<i<<"的pre和post分別為:"<<pre[i]<<" "<<post[i]<<endl;//判斷是否是有向非循環圖if(is_DAG)cout<<"圖g是有向非循環圖,沒有環"<<endl;elsecout<<"圖g不是有向非循環圖,存在環"<<endl;int k;cin>>k;return 0;}/*輸入1:正在建立無向圖...請輸入頂點個數vNum:3請輸入邊的個數eNum:3請輸入邊的頭和尾1 21 33 2輸入2:正在建立無向圖...請輸入頂點個數vNum:4請輸入邊的個數eNum:4請輸入邊的頭和尾1 22 33 44 2*/本文轉載自:http://www.cnblogs.com/xwdreamer/archive/2011/06/11/2297008.html