Files
python/TangDou/AcWing/TopSort/拓扑排序.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

94 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 拓扑排序专题
拓扑排序指的是有向无环图($DAG$);
学过计算机网络的知道计算机网络中有一个拓扑结构;
下面就是一个拓扑结构;
那拓扑序就是,图中任意一对顶点$u$和$v$,若边$<u,v>∈E(G)$,则$u$在线性序列中出现在$v$之前
我们可以发现 **拓扑序不是唯一的**;
接下来,我们需要知道一个概念——**度**:
对于有向图的某个结点来说,我们**把指向它的边的数量叫做入度**;
把**从它发出的边的数量称为出度**,这个都很好理解吧;
<center><img src='https://img-blog.csdnimg.cn/7a728ff23eeb4ee3bfc3cc0690dade3d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5piv6aW_5qKm5ZWK,size_19,color_FFFFFF,t_70,g_se,x_16'></center>
例如,下面这个图:
<div align=center>
<img src='https://img2020.cnblogs.com/blog/8562/202108/8562-20210810105201527-699576647.jpg'>
</div>
它是一个 $DAG$ 图,那么如何写出它的拓扑排序呢?这里说一种比较常用的方法:
1. 从 $DAG$ 图中选择一个没有前驱(即入度为$0$)的顶点并输出。
2. 从图中删除该顶点和所有以它为起点的有向边。
3. 重复 $1$ 和 $2$ 直到当前的 $DAG$ 图为空或 **当前图中不存在无前驱的顶点为止**。
<div align=center>
<img src='https://img2020.cnblogs.com/blog/8562/202108/8562-20210810105301779-468967491.jpg'>
</div>
于是,得到拓扑排序后的结果是{ $1,2,4,3,5$ }。
通常,一个有向无环图可以有**一个或多个**拓扑排序序列。这是因为可能同时存在多个入度为$0$的结点,这时,先处理哪个都是可以的。
#### 如何获得一个拓扑序:
我们先来找一个 **最容易理解的例子**,那就是食物链,对一个自然界的食物链来说,一定存在拓扑序;
第一:**不存在环**
第二:**有向**
接下来我们来看看如何获得一个拓扑序,我们观察最左面的结点,一定是没有指向它的边,也就是入度为零,最右边的结点呢,是一定不存在它指向别人的边的,如果有,那么它就不是最右边的点也就是出度为零;
那就是说,所有入度为零的点都可以作为起点,我们开一个数组记录入度的值;
至于如何使用邻接表存边,这就不展开解释了,可以参考这个:[传送门](https://blog.csdn.net/qq_47982709/article/details/123910900)
其实,拓扑排序也是$bfs$的一个简单应用,我们需要 **借助队列** 来实现;
首先,我们遍历存储入度的数组,获得可以作为起点的结点,将其加入队列;
接下来就可以愉快的遍历了,没当我们遍历到一个点的时候,我们让它的入度--;
这样做的 **意义** 就是,判断指向这个点的边是不是都遍历过了,因为我们要保证拓扑序最重要的一个特点:$<u,v>$的边中,$u$一定在$v$的前面出现;
如果这个点所有的边都遍历过的话,是不是也就是说这个点已经没有指向它的边了,也就是说这个点可以作为一个起点了,那我们将它加入队列;循环这个操作,知道队列为空;
![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2022/11/946c1ac7f6f4c1ac9f8208cc99439175.png)
#### 按最小的字典序输出拓扑排序
  一个图的拓扑排序往往不只有一种,所以有时题目 **要求输出字典序最小的拓扑排序**,这时我们只需要把$bfs$中的队列 **换成优先队列** 就好,同时入度为$0$的点字典序小的先出队。
### 题单
**[$AcWing$ $848$. 有向图的拓扑序列](https://www.cnblogs.com/littlehb/p/15316385.html)**
**【判断是不是拓扑图的方法,输出任意一个拓扑序列】**
**[$AcWing$ $1191$. 家谱树](https://www.cnblogs.com/littlehb/p/16013770.html)**
**【输出任意一个拓扑序列】**
**[$P4017$ 最大食物链计数 - 洛谷](https://www.cnblogs.com/littlehb/p/17622127.html)**
**【拓扑序、递推、记录入度的同时记录出度,方便找到终点、数字三角形模型】**
**[$P1137$ 旅行计划](https://www.cnblogs.com/littlehb/p/17622132.html)**
**【拓扑序、递推、记录入度的同时记录出度,方便找到终点、数字三角形模型】**
**[$AcWing$ $1192$. 奖金](https://www.cnblogs.com/littlehb/p/16115832.html)**
**【拓扑序,给初始值,按拓扑序求最长路,得最小值】**
**[$AcWing$ $164$. 可达性统计](https://www.cnblogs.com/littlehb/p/16116855.html)**
**【拓扑序,逆拓扑序倒推,$bitset$状态压缩,或运算求和】**
**[$AcWing$ $456$. 车站分级](https://www.cnblogs.com/littlehb/p/15151946.html)**
**【拓扑序,笛卡尔积条边使用中间点降维建图技巧、最长路、最小值】**