拓补排序
#include <bits/stdc++.h>
using namespace std;
void topo()
{
for(int i=1;i<=n;i++)
{
//入度为0,则加入 答案 和 入度为0的点中
if(IN[i]==0)
{
zero_in.push_back(i);
ans.push_back(i);
}
}
while(!zero_in.empty())
{
int zp=zero_in.front();
zero_in.erase(zero_in.begin());
//取出 入度为0的点,并删除
for(int i=head[zp];i;i=edge[i].nxt)
{
int to=edge[i].to;
//删除该点后,与之相连的子节点的入度就少了1
IN[to]--;
//删去该点后,子节点可能出现新的入度为0的点
if(IN[to]==0)
{
zero_in.push_back(to);
ans.push_back(to);
}
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
while(true)
{
cin>>to;
if(to==0) break;
add(i,to);
//记录每个点的入度
IN[to]++;
}
}
topo();
return 0;
}
运用:
首先,拓扑排序是针对有向无环图(DAG)的,用来对顶点进行线性排序,使得所有的有向边都从排在前面的顶点指向后面的顶点。
通俗地讲,就是完成 C 之前要先完成 B ,完成 B 之前要完成 A ......
拓补排序就是把这个顺序从先到后排列出来。
具体类型大致有以下几类
-
任务调度与依赖管理
问题类型:确定任务的执行顺序,满足所有前置依赖。
例子:
课程安排:如“课程表”问题(LeetCode 207、210),给定课程间的先修关系,判断能否完成所有课程,并输出合理的学习顺序。
项目管理:任务A必须在任务B之前完成,求可行的执行顺序。
编译依赖:确定代码模块的编译顺序(如Makefile)。 -
检测有向图中的环
问题类型:判断图中是否存在循环依赖。
例子:
如果无法生成拓扑排序(排序结果包含的节点数 < 总节点数),则图中存在环。
应用场景:死锁检测(如资源循环请求)、依赖冲突分析。 -
计算DAG的最长路径
问题类型:在DAG中,结合拓扑排序和动态规划计算最长路径。
例子:
矩阵中的最长递增路径(LeetCode 329)。
关键路径问题:项目管理中估算最短完成时间。
旅行计划 -
实际场景中的顺序规划
问题类型:解决需要严格先后顺序的问题。
例子:
工序安排:工厂流水线中工序的先后顺序。
菜谱步骤:某些烹饪步骤必须在其他步骤之前完成(如先煮水再泡茶)。

浙公网安备 33010602011771号