问题描述
有向无环图DAG可用来表示各种事物的顺序。比如以各项工作为顶点,用有向边来表示工作顺序。如果对这种表示顺序关系的DAG进行拓扑排序,我们便能得到一个恰当的工作顺序。对于一个有向无环图DAG,只要存在边(u, v),就让u在线性序列中位于v之前,这就是拓扑排序。
请编写一个程序,输出对给定DAG G进行拓扑排序后的顶点顺序。
输入:
输入按照以下形式给出
∣
V
∣
|V|
∣V∣
∣
E
∣
|E|
∣E∣
s
0
s_0
s0
t
0
t_0
t0
s
1
s_1
s1
t
1
t_1
t1
…
s
∣
E
∣
−
1
s_{|E|-1}
s∣E∣−1
t
∣
E
∣
−
1
t_{|E|-1}
t∣E∣−1
其中|V|、|E|分别代表图G的顶点数和边数。图G的各顶点编号分别为0, 1, …, |V| - 1。
s
i
s_i
si、
t
i
t_i
ti表示图G第 i 条有向边连接的2个顶点的编号。
输出:
按拓扑排序后的顺序输出图G的顶点编号。每个顶点编号占1行。
限制:
1 ≤ |V| ≤ 10000
0 ≤ |E| ≤ 100000
图G为DAG
图G不存在多重边
图G不存在自身循环。
输入示例
6 6
0 1
1 2
3 1
3 4
4 5
5 2
输出示例
0
3
1
4
5
2
讲解
所谓图的拓扑排序,就是让图中全部有向边都由左指向右,同时将所有顶点排列在一条水平线上。这样只要从左向右按顺序执行工作,就能保证执行当前工作时已完成所有准备工作了(准备工作存在)。
应用深度优先搜索或广度优先搜索能较简单地实现拓扑排序。
广度优先搜索实现的拓扑排序:
topologicalSort()
将所有的结点的color[u]设置为WHITE
设置所有结点u的入度indeg[u]
for u 从 0 至 |V| - 1
if indeg[u] == 0 && color[u] == WHITE
bfs(u)
bfs(ints)
Q.push(s)
color[s] = GRAY
while Q 不为空
u = Q.dequeue()
out.push_back(u) //将度为0的顶点加入链表
for 与 u 相邻的结点v
indeg[v]--
if indeg[v] == 0 && color[v] == WHITE
color[v] = GRAY
Q.enqueue(v)
上述算法根据广度优先搜索的顺序依次访问入度为0的顶点,并将访问过的顶点添加至链表末尾。
该算法将访问过的顶点u视为已结束,同时将下一顶点v(从v出发的边指向的顶点)的入度减1。这一操作相当于删除边。不断地删除边可以使v的入度逐渐将为0,此时我们便可以访问顶点v,然后将v加入链表。
深度优先搜索实现的拓扑排序:
topologicalSort()
将所有结点的color[u]设置为WHITE
for s从0至|V| - 1
if color[s] == WHITE
dfs(s)
dfs(u)
color[u] = GRAY
for 与u相邻的结点v
if color[u] == WHITE
dfs(v)
out.push_front(u) //将访问结束的顶点逆向添加至链表
上述算法通过深度优先搜索访问顶点,并把访问完的顶点添加至链表开头。这里要注意,由于深度优先搜索是逆向确定各顶点的拓扑排序,因此顶点是添加至链表开头的。
用深度和广度的复杂度同为 O ( ∣ V ∣ + ∣ E ∣ ) O(|V|+|E|) O(∣V∣+∣E∣)。考虑到大规模图容易引起溢出,因此不涉及递归的广度优先搜索更为合适。
AC代码如下
广度优先搜索实现
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<list>
using namespace std;
static const int MAX = 100000;
static const int INFTY = (1<<29);
vector<int> G[MAX];
list<int> out;
bool V[MAX];
int N;
int indeg[MAX];
void bfs(int s){
queue<int> q;
q.push(s);
V[s] = true;
while(!q.empty() ){
int u = q.front(); q.pop();
out.push_back(u);
for(int i = 0; i < G[u].size(); i++){
int v = G[u][i];
indeg[v]--;
if(indeg[v] == 0 && !V[v]){
V[v] = true;
q.push(v);
}
}
}
}
void tsort(){
for(int i = 0; i < N; i++){
indeg[i] = 0;
}
for(int u = 0; u < N; u++){
for(int i = 0; i < G[u].size(); i++){
int v = G[u][i];
indeg[v]++;
}
}
for(int u = 0; u < N; u++)
if(indeg[u] == 0 && !V[u]) bfs(u);
for(list<int>::iterator it = out.begin(); it != out.end(); it++){
cout<<*it<<endl;
}
}
int main(){
int s, t, M;
cin>>N>>M;
for(int i = 0; i < N; i++) V[i] = false;
for(int i = 0; i < M; i++){
cin>>s>>t;
G[s].push_back(t);
}
tsort();
return 0;
}
深度优先搜索实现
#include<iostream>
#include<vector>
#include<algorithm>
#include<list>
using namespace std;
static const int MAX = 100000;
vector<int> G[MAX];
list<int> out;
bool V[MAX];
int N;
void dfs(int u){
V[u] = true;
for(int i = 0; i < G[u].size(); i++){
int v = G[u][i];
if(!V[v]) dfs(v);
}
out.push_front(u);
}
int main(){
int s, t, M;
cin>>N>>M;
for(int i = 0; i < N; i++) V[i] = false;
for(int i = 0; i < M; i++){
cin>>s>>t;
G[s].push_back(t);
}
for(int i = 0; i < N; i++){
if(!V[i]) dfs(i);
}
for(list<int>::iterator it = out.begin(); it != out.end(); it++)
cout<<*it<<endl;
return 0;
}