拓撲排序【Kahn算法(bfs)和dfs求拓撲序列及判環】

拓撲排序node

對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中全部頂點排成一個線性序列,該排序知足這樣的條件——對於圖中的任意兩個結點uv,若存在一條有向邊從u指向v,則在拓撲排序中u必定出如今v前面。
 
當且僅當一個有向圖爲有向無環圖(DAG)時才存在對應於該圖的拓撲排序。每個有向無環圖都至少存在一種拓撲排序。
 
拓撲排序的實現:
①Kahn算法【常可用來判斷該圖是不是DAG(有向無環圖)】
算法複雜度爲O(v+e)。
算法實現:循環執行如下兩步直到不存在入度爲0的頂點爲止。
(1)選擇一個入度爲0的頂點並輸出之;(2)從網中刪除此頂點及其全部出邊。
循環結束後,若輸出的頂點數小於網中的頂點數,則該圖存在迴路,不然輸出的頂點序列就是拓撲序列,該圖也即爲DAG圖。
 
該算法可藉助隊列實現。相似於bfs:首先將入度爲0的頂點入隊,取出隊頭元素進行拓展,找其鄰接點,每一個鄰接點的入度-1,當入度變爲0時則入隊。循環此操做直到隊列爲空。出隊頂點序列即爲拓撲序列。
int k=0; void toposort() { queue<int>q; for(int i=1;i<=n;i++) if(!indegree[i]) q.push(i); while(!q.empty()){ int u=q.front(); q.pop(); cout<<u<<' ';k++;//輸出拓撲序列,計數+1
    for (u的每一個鄰接點v){ //vector或鏈式前向星建圖,遍歷方式不同
        indegree[v]--;//刪除邊(u, v),即讓v的入度-1;
        if (!indegree(v) ) q.push(v);   } } } if (k!=n) 存在環; else 不存在環,爲DAG圖; 

②基於DFS的拓撲排序算法( 複雜度O(V+E) )ios

1)DFS求給定DAG圖的拓撲序列【前提:已知圖是DAG】c++

topo數組存求得的拓撲序列 int k=0; void DFS(int x) { vis[x]=1; for(遍歷x的鄰接點j){ if(!vis[j]) DFS(j); } topo[k++]=x;//用棧存
} void toposort() { for(int i=1;i<=n;i++){ //遍歷每一個頂點
        if(!vis[i]) DFS(i); } for(int j=k-1;j>=0;j--) cout<<topo[i]<<' ';//逆序輸出數組,所得序列即爲拓撲序列
}

2)DFS判斷有向圖是否有環(是否存在合法的拓撲序列):算法

對一個節點x進行dfs,判斷是否能從x回到本身這個節點,便是否存在x到x的迴路。數組

這裏需用一個color數組標記節點的狀態:-1表明未訪問,0表明正在被訪問,1表明已被訪問過spa

#include<bits/stdc++.h>
using namespace std; int n,e;//n個頂點(1<=n<=100),e條邊
int color[105];//color數組表示每一個結點的狀態
vector<int>G[105];//vector鄰接表建圖
bool flag;//爲真則存在環
void DFS(int x) { if(flag)//若是有環就返回,否者繼續搜索
        return; color[x]=0;//x正在被訪問,狀態爲0
    for(int i=0;i<G[x].size();i++){ if(color[G[x][i]]==-1)//與x相連的結點狀態爲-1,則該節點未被訪問,繼續搜索
 dfs(G[x][i]); else if(color[G[x][i]]==0){//與x相連的結點狀態也爲0,表明有環,返回
            flag=true; return; } } color[x]=1;//標記x狀態爲已被訪問過了
} int main() { int u,v; while(~scanf("%d%d",&n,&e)) { flag=false; for(int i=1;i<=n;i++)//二維vector初始化
            for(int j=0;j<G[i].size();j++) G[i].pop_back(); memset(color,-1,sizeof(color));//初始化爲未訪問
        
        for(int i=1;i<=e;i++){ scanf("%d%d",&u,&v); G[u].push_back(v); } DFS(1); if(!flag) printf("有向圖無環,存在合法的拓撲序列\n"); else printf("有向圖有環,不存在合法的拓撲序列\n"); } return 0; }

 

UVA10305 給任務排序 Ordering Tasks.net

題意code

John有n個任務要作,每一個任務在作以前要先作特定的一些任務。blog

輸入第一行包含兩個整數n和m,其中1<=n<=100。 n表示任務數,而m表示有m條任務之間的關係。 接下來有m行,每行包含兩個整數i和j,表示任務i要在j以前作。排序

當讀入兩個0(i=0,j=0)時,輸入結束。

輸出包含q行,每行輸出一條可行的安排方案。

輸入:
5 4 1 2 2 3 1 3 1 5 0 0
輸出:
1 4 2 5 3

題解

裸拓撲排序

Code

Kahn算法+vector鄰接表

#include<cstdio> #include<cstring> #include<queue> #include<vector>
using namespace std; int n,m,indegree[105]; void toposort(vector<vector<int> >G) { int space=0; queue<int>q; for(int i=1;i<=n;i++) if(!indegree[i]) q.push(i); while(!q.empty()){ int x=q.front();q.pop(); space?printf(" "):printf(""),space=1; printf("%d",x); for(int j=0;j<G[x].size();j++){ indegree[G[x][j]]--; if(!indegree[G[x][j]]) q.push(G[x][j]); } } } int main() { int u,v; while(~scanf("%d%d",&n,&m)&&n) { vector<vector<int> >G(n+5);//開n+5行的vector二維數組
        indegree[105]={}; while(m--){ scanf("%d%d",&u,&v); G[u].push_back(v); indegree[v]++; } toposort(G); puts(""); } return 0; }

 基於DFS的拓撲排序+鏈式前向星建圖

#include<cstdio> #include<cstring> #include<iostream> #include<queue>
using namespace std; struct node{ int to,next; }G[105]; int head[105],cnt; void add(int u,int v) { G[cnt].to=v; G[cnt].next=head[u]; head[u]=cnt++; } int n,e,indegree[105],ans[105],k,vis[105]; void DFS(int rec) { vis[rec]=1; for(int j=head[rec];~j;j=G[j].next){ if(!vis[G[j].to]) DFS(G[j].to); } ans[k++]=rec; } void toposort() { for(int i=1;i<=n;i++){ if(!vis[i])DFS(i); } int q=1; for(;k>0;){ q?cout<<"":cout<<" "; q=0; cout<<ans[--k]; } } int main() { int u,v; while(~scanf("%d%d",&n,&e)&&n) { cnt=0; memset(vis,0,sizeof(vis)); memset(head,-1,sizeof(head)); for(int i=1;i<=e;i++){ scanf("%d%d",&u,&v); add(u,v); } toposort(); puts(""); } return 0; }
相關文章
相關標籤/搜索