拓扑排序与判断有向图是否有回路


拓扑排序与判断有向图是否有环

方式1:基于BFS:采用入度的方式判断是否有回路

 

  • 定义队列Q,将所有入度为0的结点加入队列

  • 取出队列的首节点,输出,然后删去从它出发的所有边,并令边的另一端结点的入度减1,如果减到了0,就将其加入队列

  • 重复上面一个操作,直到队列为空。

  • 队列为空时,如果入过队列的结点数为N,则拓扑排序成功,图为有向无环图;否则图中有环

 

 

//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;
}
View Code

 

测试数据:

//左图
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 
View Code

              

 

 

二、判断有向图是否有环(基于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;
}
View Code

 

测试数据:

//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
View Code

   

 

 

三、基于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;
}
View Code

 

测试数据:

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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM