拓扑排序
拓扑排序
大家好,我是Weekoder!
接上次的二分查找,我又打算写一篇关于拓扑排序的文章!
本文涉及到的知识比较多,请确认已经掌握了以下知识:
其中,第二条并不是必要的,只要你能用自己的方法遍历图上任意一个点所有出边,如使用链式前向星。
好了,当你掌握了这些知识后,我们就可以进入主题了。
拓扑排序的理论概念
在有向无环图(DAG)中,将
拓扑排序的作用
当我们做事情时,往往需要有一个先后顺序。就像早上你需要先起床,再洗脸刷牙,换衣服,然后吃早餐,最后出门。你总不能先出门再起床吧。我们把这些需要处理的事情看作图上的点,事情之间的关系看作一条有向边,这种先后顺序就是拓扑序。这只是一个简单的例子,但如果你有
而又因为洗脸刷牙和换衣服不分先后顺序,所以拓扑序不一定是唯一的。
提问/解释环节
- 有向无环图是啥?
由有向边组成的没有环的图叫有向无环图,又叫DAG。
- 没什么非得是有向无环图?
有向:
因为我们需要满足对于每一条边
无环:
我们再来举个例子。假设有两个需要处理的事情
可以看到,一条
实现拓扑排序
题目大意:
给出
分析:
既然是要整理出一种顺序,不妨试试拓扑排序。我们可以把每个人看作一个结点,并且因为要先输出辈分大的,所以将祖先指向后代来建造有向边。而且这里是一定不会出现环的,因为不可能出现我是你爸爸,然后你又是我爷爷之类的事情,因此只可能构造一个有向无环图。
代码:
先把我的代码贴上来,接下来我会跟着代码上的注释逐一讲解。
:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5; // 数组大小
int n, in[N], x; // 分别是人数,每个点的入度,输入变量
vector<int> nbr[N]; // 存图
// 先去看主函数!
void topo() { // 封装成一个函数
queue<int> q; // 用队列进行操作
for (int i = 1; i <= n; i++) // 先将所有入度为0的点入队
if (!in[i]) // 判断入度是否为0
q.push(i); // 入队
while (!q.empty()) { // 当队列不为空时执行(还有入度为0的点)
int cur = q.front(); // 先记录即将要输出并且要遍历后代的点
q.pop(); // 删除入度为0的点
cout << cur << " "; // 先输出
for (auto nxt : nbr[cur]) { // 看不懂的可以改为:for (int nxt = 0; nxt < nbr[cur].size(); nxt++) 遍历这个点的所有后代
in[nxt]--; // 入度减1
if (!in[nxt]) // 如果入度为0
q.push(nxt); // 入队
}
}
return ; // 一定要写返回!回去看主函数
}
int main() {
ios::sync_with_stdio(0); // 这个可以不用管
cin >> n; // 输入人数
for (int i = 1; i <= n; i++) { // 循环读入每个人的后代
while (1) {
cin >> x; // 输入后代
if (!x) // 如果为0则跳出
break; // 跳出
nbr[i].emplace_back(x); // 记录i的后代
in[x]++; // x的入度加1
}
}
topo(); // 去看拓扑排序函数
return 0; // 大功告成!
}
OK,我们从主函数看起。
首先是一些输入,我就不讲解了,来看
nbr[i].emplace_back(x);
in[x]++;
这两句是什么意思呢?首先,这里用到了一个
接下来就该调用我们的拓扑排序函数
在
现在我们知道了我们要找入度为
总结 + 挑战例题
拓扑排序就是一直将入度为零的点输出、删除并继续统计,最后得到拓扑序,并且这个算法的时间复杂度是
洛谷
再见!
本文作者:Weekoder
本文链接:https://www.cnblogs.com/Weekoder/p/18240250
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步