拓扑排序与判断有向图是否有环
方式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