拓撲排序與判斷有向圖是否有回路


拓撲排序與判斷有向圖是否有環

方式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