有向无环图DAG可用来表示各种事物的顺序。比如以各项工作为顶点,有向边来表示工作顺序。
上图中我们着手处理工作B之前需要先处理完成工作A和工作X,如果对这种表示顺序关系的DAG进行拓扑排序,我们便可以得到一个恰当的工作排序,对于一个有向无环图DAG,只要存在边(u,v),就让u在线性序列中位于v之前,这就是拓扑排序。
所谓图的拓扑排序,就是让图中全部有向边都由左向右,同时将所有顶点排列在一条水平线上。如上图的工作安排,就可以完成所有准备工作。
应用深度优先或广度优先能够较简单的实现拓扑排序。
下面是广度优先搜索实现拓扑排序。
#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];//indeg[i]记录i的入度数
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);
//完成u点之后要斩断与u有关的点的问题
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
//v的入度减少
indeg[v]--;
//如果点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++){
//统计结点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<N;i++){
cin>>s>>t;
G[s].push_back(t);
}
tsort();
return 0;
}
/*
6 6
0 1
1 2
3 1
3 4
4 5
5 2
0
3
1
4
5
2
*/
以下是深度优先搜索实现拓扑排序。
#include<iostream>
#include<vector>
#include<list>
#include<algorithm>
using namespace std;
static const int MAX = 100000;
int N;
list<int>out;
vector<int>G[MAX];
bool V[MAX];
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 u,v,M;
cin>>N>>M;
for(int i=0;i<N;i++){
V[i]=false;
}
for(int i=0;i<M;i++){
cin>>u>>v;
G[u].push_back(v);
}
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;
}
考察:用深度、广度优先搜索实现的拓扑排序算法复杂度同为O(|V|+|E|)。考虑到大规模图容易引起栈溢出,因此不涉及广度搜索更为合适。