拓扑排序 | Topological Sort | C/C++实现

问题描述

有向无环图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} sE1 t ∣ E ∣ − 1 t_{|E|-1} tE1
其中|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;
}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值