拓扑排序,其实就是根据一定的优先级信息,将信息在图这种抽象出来的数据结构中集成,最后生成的顺序。


下面来看一道模板题,来自洛谷的B3644。

B3644 【模板】拓扑排序 / 家谱树

拓扑排序是dfs和bfs在图上的经典应用,本文以笔记的形式,通过bfs来实现拓扑排序。

在实现拓扑排序之前,我们需要知道几个概念,即图节点的入度和出度。

如上图,节点1的入度为3,出度为0,节点2的入度为0,出度为2,,很显然,在有向图中,入度为零的节点优先级最高,排在最首,出度为0的节点优先级最低,排在最尾(2>1),方向是从优先级高指向优先级低(或者反过来也可以),当然,如果将所给的信息放在有向图中后,发现出现了环,那么不存在优先级最高(最低)的节点,也就不能进行排序,也就是说,拓扑排序只能在有向无环图中进行。(如下图)

使用bfs实现拓扑排序,可以从入度考虑,也可以从出度考虑,分别对应两种原理:

1、无前驱的节点优先。入度为零的节点优先级最高,记录,删除其与相邻节点的边,再找入度为零的节点(它在剩下的节点中优先级最高),记录,直到找完所有节点,如果遇到多个入度为零的节点,则它们之间的顺序不确定,造成了拓扑排序的不确定性(多解)。

2、无后继的节点优先。按出度思考同理,每次找优先级最低的节点。

有没有感觉到这其实类似于选择排序(堆排序)的思想?


下面是C/C++代码实现:

#include<bits/stdc++.h> 
using namespace std;
const int N=2e3+10;
//#define int long long
//基于广度优先搜索实现的拓扑排序
int n,h[N],ans[N],k;
queue<int> q;
struct edge
{
	int from,to;
	edge(int a,int b){from=a;to=b;}
};
vector<edge> a[N]; // 邻接表存图 
void init()
{
	for(int i=1;i<=n;i++) a[i].clear();
	return;
}
signed main()
{
    scanf("%d",&n);
    init();
    for(int i=1;i<=n;i++)
    {
		int re;
		while((scanf("%d",&re),re))
		{
			a[i].push_back(edge(i,re));
			h[re]++; // 保存入度 
		}
	} // 读入图
	for(int i=1;i<=n;i++) if(!h[i]) q.push(i);
	while(!q.empty())
	{
		int b=q.front();
		q.pop();
		ans[k++]=b;
		for(int i=0;i<(int)a[b].size();i++) //遍历所有邻居 
		{
			h[a[b][i].to]--; //邻居入度减一 
			if(!h[a[b][i].to]) q.push(a[b][i].to); // 入度减为零的入队 
		}
	}
	for(int i=0;i<k;i++) printf("%d ",ans[i]);
	return 0;
}

更多推荐