0
点赞
收藏
分享

微信扫一扫

图论 —— 拓扑排序

老牛走世界 2022-02-11 阅读 97

拓扑排序基本概念

在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:

  1. 每个顶点出现且只出现一次。
  2. 若存在一条从顶点 A A A 到顶点 B B B 的路径,那么在序列中顶点 A A A 出现在顶点 B B B 的前面。

有向无环图(DAG)才有拓扑排序,非 DAG 图没有拓扑排序一说。
例如,下面这个图,
在这里插入图片描述
它是一个 DAG 图,那么如何写出它的拓扑排序呢?这里说一种比较常用的方法:

  1. 从 DAG 图中选择一个 没有前驱(即入度为 0 0 0)的顶点并输出。
  2. 从图中删除该顶点和所有以它为起点的有向边。

重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。
在这里插入图片描述于是,得到拓扑排序后的结果是 { a , c , b , f , d , e } \{a, c, b, f, d,e\} {a,c,b,f,d,e}
通常,一个有向无环图可以有一个或多个拓扑排序序列。

拓扑排序实现

关键是要维护一个入度为 0 0 0 的顶点的集合。
Step 1:将所有入度为 0 0 0 的顶点入队。
Step 2:遍历队列,获取队头的顶点 u u u,打印输出 u u u
Step 3:枚举 u u u 的每一条出边 v v v,删除 u → v u \to v uv 的边,就是将 in[v] 减 1 1 1
Step 4:如果当前 v v v 的入度为 0 0 0,将 v v v 加入队列。
Step 5:重复 Step 2,3,4,直到队列为空为止。

数据结构

稠密图可以使用邻接矩阵。稀疏图可以使用邻接表。
大部分情况下,我们使用邻接表。

const int N=1e5+10;
LL h[N];
LL in[N];//顶点入度
const int M=1e6+10;
LL e[M], ne[M], idx;
//队列相关
LL que[M];
LL hh=0;
LL tt=-1;
LL n;//保存顶点数量

初始化

memset(h, -1, sizeof h);

插入边

void add(LL a, LL b) {
	//a -> b
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
	//入度加
	in[b]++;
}

拓扑排序

bool top_sort() {
	//初始化队列
	hh=0; tt=-1;
	
	//将所有入度为零的加入队列
	for (LL i=1; i<=n; i++) {
		if (in[i]==0) {
			que[++tt]=i;//入队
		}
	}

	while (hh<=tt) {
		LL u=que[hh];//获取队头
		hh++;//出队

		//遍历u的所有出边
		for (LL i=h[u]; i!=-1; i=ne[i]) {
			LL v=e[i];
			in[v]--;//出度减
			if (in[v]==0) {
				que[++tt]=v;
			}
		}
	}

	//判断是会否是 DAG,
	return tt==n-1;
}

注意,出队的顺序就是一个拓扑序列。也就是说 que 里面的数据就是一个拓扑排序。如果需要输出对应的拓扑排序,从 0 0 0 n − 1 n-1 n1 就是一个拓扑序。
另外要注意,拓扑序不是唯一的。

举报

相关推荐

0 条评论