专栏文章

拓扑排序 Topo

算法·理论参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min5c08m
此快照首次捕获于
2025/12/01 20:49
3 个月前
此快照最后确认于
2025/12/01 20:49
3 个月前
查看原文

定义

拓扑排序(Topological sorting)是在 DAG(有向无环图) 中对其顶点的一种线性排序,使得对于从顶点 u{\displaystyle u} 到顶点 v{\displaystyle v} 的每个有向边 uv{\displaystyle u\to v}u{\displaystyle u} 在排序中都在 v{\displaystyle v} 之前。
这就像是在给一系列有 依赖关系 的任务进行排序。比如“穿裙子”这个任务,依赖于“先穿内衣”。那么拓扑排序的结果中,“穿内衣”必然在“穿裙子”之前。

Kahn

非常直观的算法。模拟了一个“不断移除没有前置依赖的节点”的过程。
算法步骤:
  1. 初始化:
    • 计算图中每个节点的入度(即有多少条边指向它);
    • 创建一个队列,将所有入度为 00 的节点加入其中。这些节点是“起点”,它们不依赖于任何其他任务;
    • 初始化一个空列表,用于存放拓扑排序的结果。
  2. 循环处理:
    • 从队列中取出一个节点 uu,并将其加入结果列表;
    • 遍历 uu 的所有指向的节点 vv
    • vv 的入度减 11
    • 如果此时 vv 的入度变为 00,说明 vv 的所有前置依赖都已被满足,则将 vv 加入队列;
    • 重复此过程,直到队列为空。
  3. 结束检查:
    • 如果结果列表中的节点个数与图中总节点数相同,说明排序成功,返回该列表。
    • 如果结果列表中的节点个数少于总节点数,说明图中存在环,无法进行拓扑排序。
代码实现
CPP
#include<bits/stdc++.h>
using namespace std;

const int MAX = 105;
queue<int> q;
vector<int> g[MAX], ans;
int rd[MAX], n;

void kahn()
{
	for(int i=1; i<=n; i++)
		if(rd[i] == 0) q.push(i);
	while(!q.empty())
	{
		int t = q.front(); q.pop();
		ans.push_back(t);
		for(auto v : g[t])
		{
			rd[v]--;
			if(rd[v] == 0) q.push(v);
		}
	}
	if(ans.size() != n) cout << "有环" << endl;
	else for(auto v : ans) cout << v << ' ';
}

int main()
{
	cin >> n;
	for(int i=1; i<=n; i++)
	{
		int a; cin >> a;
		while(a) {g[i].push_back(a), rd[a]++; cin >> a;}
	}
	kahn();
	return 0;
}

DFS

利用深度优先搜索,在回溯时记录节点,本质上是一种后序遍历
算法步骤:
  1. 对图中的每个未访问节点执行DFS。
  2. 在DFS过程中:
    • 递归地访问它所有指向的节点。
    • 当某个节点的所有邻居都处理完毕后,将该节点压入一个栈。
  3. 最终,栈顶到栈底(或列表从头到尾)的顺序就是一个拓扑排序。
代码实现(未检测环)
CPP
#include<bits/stdc++.h>
using namespace std;
const int MAX = 105;
vector<int> e[MAX];
int n, vis[MAX];

stack<int> ans;
void dfs(int u)
{
    for(auto v : e[u]) {if(vis[v]) continue; dfs(v);}
    vis[u] = 1; ans.push(u);
}

signed main()
{
    int n, a; cin >> n;
    for(int i=1; i<=n; i++)
        while(cin >> a && a)
            e[i].push_back(a);
    for(int i=1; i<=n; i++) if(!vis[i]) dfs(i);
    while(!ans.empty()) cout << ans.top() << ' ', ans.pop(); 
    return 0;
}
解析:
  1. 先递归处理所有后继节点(依赖当前节点的节点)
  2. 当所有后继节点都处理完后,再处理当前节点
  3. 使用栈来保证"依赖关系"的正确性

总结

特性KahnDFS
思想BFS,基于入度DFS,基于递归栈
环检测简单直观需要额外状态标记
空间队列+入度数组递归栈+状态数组
适用场景拓扑排序dfs强强的人使用

评论

0 条评论,欢迎与作者交流。

正在加载评论...