文章目录
拓扑排序
1. 算法分析
1.1 特点分析
拓扑排序可以在线性的时间复杂度 O(n + m) 内完成求出拓扑序的操作,对象是有向无环图。
拓扑图的性质如下:
- 有向图才有拓扑序
- 有向无环图必定存在拓扑序
- 存在拓扑序 <=> 无环
- 有向无环图至少存在一个入度为0的点
- 当前的点只影响后面的状态,所以可以dp处理
1.2 使用场景
拓扑排序,可以支持以下操作:
- 求出拓扑序:
1.1 求一般拓扑序:如果是一般队列,那么求出的为一般的拓扑序
1.2 求字典序最大/最小拓扑序:如果是优先队列,那么求出的是字典序最大/最小拓扑序 - 拓扑序判断环
判断图中是否有环:如果原来的点数==最后拓扑序内的点数,那么存在拓扑序,无环;否则,有环 - 拓扑序+dp:
3.1 求最短\长路:如果边权全部大于0,那么可以使用拓扑排序找最短路
3.2 求每个点的可达性
2. 例题
2.1 求出拓扑序
2.1.1 一般拓扑序
#include <bits/stdc++.h>
using namespace std;
int const N = 1e5 + 10;
int e[N], ne[N], h[N], idx, d[N];
int n, m;
vector<int> ans;
// 建立邻接表
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 拓扑排序
void top_sort() {
queue<int> q; // 维护一个队列
for (int i = 1; i <= n; ++i) if (!d[i]) q.push(i); // 把入度为0的点加入队列
// 当队列不为空时
while (q.size()) {
auto t = q.front(); // 取队头
q.pop(); // 队头出队
ans.push_back(t); // 把这个数字放入答案序列
for (int i = h[t]; i != -1; i = ne[i]) {
// 枚举所有队头元素相邻的元素
int j = e[i];
d[j]--; // 队头元素出队相当于把与队头元素相连的元素的入度减一
if (!d[j]) q.push(j); // 把入度为0的元素放入队列
}
}
if (ans.size() == n) {
// 输出答案序列
for (auto a: ans) printf("%d ", a);
}
else cout << "-1";
}
int main()
{
cin >> n >> m; // 输入点数和边数
memset(h, -1, sizeof h); // 初始化h
for (int i = 0; i < m; ++i) {
// 读入每条边
int a, b;
scanf("%d %d", &a, &b);
add(a, b); // 把b插入a的边表
d[b]++; // b的入度加一
}
top_sort();
return 0;
}
2.1.2 求出字典序最大/最小的拓扑序
#include <bits/stdc++.h>
using namespace std;
int const N = 1e5 + 10;
int e[N], ne[N], h[N], idx, d[N];
int n, m;
vector<int> ans;
// 建立邻接表
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 拓扑排序
void top_sort() {
priority_queue<int, vector<int>, greater<int>> q; // 这里是求字典序最小的拓扑序,如果求字典序最大的,那么改成 priority_queue<int, vector<int>, less<int>> q;
for (int i = 1; i <= n; ++i) if (!d[i]) q.push(i); // 把入度为0的点加入队列
// 当队列不为空时
while (q.size()) {
auto t = q.top(); // 取队头
q.pop(); // 队头出队
ans.push_back(t); // 把这个数字放入答案序列
for (int i = h[t]; i != -1; i = ne[i]) {
// 枚举所有队头元素相邻的元素
int j = e[i];
d[j]--; // 队头元素出队相当于把与队头元素相连的元素的入度减一
if (!d[j]) q.push(j); // 把入度为0的元素放入队列
}
}
for (int i = 0; i < ans.size(); ++i) {
cout << ans[i];
if (i != ans.size() - 1) cout << " ";
}
cout << endl;
}
int main() {
while (scanf("%d%d", &n, &m) !=

本文详细介绍了拓扑排序的概念,包括其特点和使用场景。拓扑排序可以在有向无环图中找到线性的拓扑序,可用于求解拓扑序、判断环以及结合动态规划解决最短路等问题。文章通过实例解析了如何利用拓扑排序求解具体题目,并提供了相关代码示例。
最低0.47元/天 解锁文章
797

被折叠的 条评论
为什么被折叠?



