拓撲排序與判斷有向圖是否有環
方式1:基於BFS:采用入度的方式判斷是否有回路
-
-
取出隊列的首節點,輸出,然后刪去從它出發的所有邊,並令邊的另一端結點的入度減1,如果減到了0,就將其加入隊列
-
重復上面一個操作,直到隊列為空。
-
//create on 20210216 //拓撲排序;判斷圖中是否有環 //基於判斷結點入度 //鄰接表存儲;模板 #include<iostream> #include<vector> #include<queue> #define maxn 501 using namespace std; vector<int> edge[maxn]; //存儲每個結點的下一個結點(有向圖),忽略權重 queue<int> q; //保存入度為0的結點 int indegree[maxn]; //存儲每個結點的入度 int main(){ int n,m; //n為結點個數;m為邊的個數 cin>>n>>m; for(int i=0;i<m;i++){ int a,b; cin>>a>>b; edge[a].push_back(b); indegree[b]++; } int cnt = 0; for(int i=1;i<=n;i++){ if(indegree[i]==0){ q.push(i); //所有入度為0的結點進入隊列 cnt++; //統計每個如果隊列的結點個數 } } while(!q.empty()){ int node = q.front(); q.pop(); for(int i=0;i<edge[node].size();i++){ indegree[ edge[node][i] ]--; if(indegree[ edge[node][i] ]==0){ q.push(edge[node][i]); cnt++; } } } if(cnt == n) cout<<"NO LOOP"<<endl; else cout<<"LOOP EXISTS"<<endl; return 0; }
方式二:基於DFS
一、基於dfs的拓撲排序(不能處理有環的情況)
代碼實現:(20191205)

/* 狀態0是還沒有被訪問 狀態 1是此次dfs結束,也就是說是最小元,后面沒有結點了,才能被標記為1,然后入棧 */ #include<iostream> #include<stack> #define inf 9999999 using namespace std; int book[101],sum,n,e[101][101]; stack<int> s; void dfs (int cur){ for(int i=1;i<=n;i++) if(e[cur][i]==1&&book[i]==0) dfs(i); book[cur] = 1;//與DFS的區別在於 標記當前節點的時間 (在遞歸結束才加入) s.push(cur); return; } int main(){ int i,j,m,a,b; cin>>n>>m; for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ if(i==j) e[i][j]=0; else e[i][j]=inf; } } for(i=1;i<=m;i++){ cin>>a>>b; e[a][b]=1; } dfs(2); while(!s.empty()){ cout<<s.top()<<" "; s.pop(); } return 0; }
測試數據:

//左圖 9 9 2 4 2 5 2 1 1 3 3 6 3 8 5 8 5 7 8 9 //右圖 7 8 1 2 1 3 2 4 3 4 2 6 2 5 4 5 5 7
二、判斷有向圖是否有環(基於dfs拓撲排序)
算法描述:每個結點有三個狀態:狀態0是還沒有被訪問;狀態-1是在這次dfs過程中被標記;
狀態 1是此次dfs結束,也就是說是最小元(后面沒有結點了)才能被標記為1;
如果 與 在此次dfs過程中已訪問過的結點(狀態為-1)有邊相連,說明有環(回路);
如果 與 在此次dfs過程中未訪問的結點(狀態為0)有邊相連,則繼續dfs;
此次dfs結束后(到最小元的結點),將最小元的狀態標記為1,並且return true(因為此次dfs過程中未出現環)
代碼實現:(20200129)

/* 狀態0是還沒有被訪問 狀態-1是在這次dfs過程中被標記 狀態 1是此次dfs結束,也就是說是最小元,后面沒有結點了,才能被標記為1 */ #include<iostream> #define inf 999999 using namespace std; int book[110]; int e[110][110]; int n; bool dfs (int cur){ book[cur] = -1; for(int i=1;i<=n;i++){ if(e[cur][i]!=0&&e[cur][i]!=inf&&book[i]==-1) return false; //如果 與 在此次dfs過程中已訪問過的結點(狀態為-1)有邊相連,說明有環 if(e[cur][i]!=0&&e[cur][i]!=inf&&book[i]==0&&!dfs(i)) return false; // 如果 與 在此次dfs過程中未訪問的結點(狀態為0)有邊相連,則繼續dfs } book[cur] = 1;//與DFS的區別在於 標記當前節點的時間 (在遞歸結束才加入) return true; } int main(){ cin>>n; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(i==j) e[i][j] = 0; else e[i][j] = inf; } } int m; cin>>m; int a,b,c; for(int i=0;i<m;i++){ cin>>a>>b>>c; e[a][b] = c; // directed } bool state = dfs(1); if(state) cout<<"none"; else cout<<"exist"; return 0; }
測試數據:

//none左圖 7 7 1 2 1 1 3 1 2 7 1 3 4 3 4 5 3 5 7 6 6 7 2 //exist右圖 7 7 2 1 1 1 3 1 7 2 1 3 4 3 4 5 3 5 7 6 6 7 2
三、基於dfs的拓撲排序(可處理有回路的情況)
代碼實現:(20191205)

/* 狀態0是還沒有被訪問 狀態-1是在這次dfs過程中被標記 狀態 1是此次dfs結束,也就是說是最小元,后面沒有結點了,才能被標記為1,然后入棧 */ #include<stdio.h> #include<iostream> #include<stack> #define inf 9999999 using namespace std; int book[101],sum,n,e[101][101]; stack<int> s; bool dfs (int cur){ book[cur] = -1; for(int i=1;i<=n;i++){ if(e[cur][i]==1&&book[i]==-1) return false;//如果在這個dfs過程中邊相連,說明有環 if(e[cur][i]==1&&book[i]==0&&!dfs(i)) return false; } book[cur] = 1;//與DFS的區別在於 標記當前節點的時間 (在遞歸結束才加入) s.push(cur); return true; } int main(){ int i,j,m,a,b; scanf("%d %d",&n,&m); for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ if(i==j) e[i][j]=0; else e[i][j]=inf; } } for(i=1;i<=m;i++){ scanf("%d %d",&a,&b); e[a][b]=1; } bool ans = dfs(2); if(ans == false) cout<<"Loop exists!"<<endl; else{ while(!s.empty()){ cout<<s.top()<<" "; s.pop(); } } return 0; }
測試數據:

9 10 2 4 2 5 2 1 1 3 3 6 3 8 5 8 5 7 8 9 8 1