拓扑排序是一种对有向无环图(DAG)进行排序的方法,可以用来解决任务调度、依赖关系、编译顺序等问题。
下面以一个图为例进行拓扑排序:
该图表示一个软件系统的组件依赖关系,箭头表示依赖关系,例如A依赖B和C,即A需要在B和C之前完成。
使用拓扑排序对该图进行排序的步骤如下:
-
找到所有入度为0的点,即没有依赖的点,将它们加入一个队列。
在该图中,入度为0的点为E和H。
-
从队列中取出一个入度为0的点,将其输出,并将其所有相邻节点的入度减1。
在该图中,我们从队列中取出E,输出E,并将F的入度减1,得到新的入度表:
复制插入
A: 2 B: 1 C: 1 D: 2 E: 0 F: 1 G: 1 H: 0
复制插入
-
将新的入度为0的点加入队列。
在该图中,此时新的入度为0的点为H。
-
重复步骤2和步骤3,直到队列为空。
在该图中,我们继续取出H,输出H,并将A的入度减1,得到新的入度表:
复制插入
A: 1 B: 1 C: 1 D: 2 E: 0 F: 1 G: 1 H: 0
复制插入
接着我们取出E,输出E,并将D的入度减1,得到新的入度表:
复制插入
A: 1 B: 1 C: 1 D: 1 E: 0 F: 1 G: 1 H: 0
复制插入
然后我们取出B,输出B,并将A的入度减1,得到新的入度表:
复制插入
A: 0 B: 0 C: 1 D: 1 E: 0 F: 1 G: 1 H: 0
复制插入
再取出C,输出C,并将D的入度减1,得到新的入度表:
复制插入
A: 0 B: 0 C: 0 D: 0 E: 0 F: 1 G: 1 H: 0
复制插入
然后我们取出D,输出D,并将A的入度减1,得到新的入度表:
复制插入
A: -1 B: 0 C: 0 D: 0 E: 0 F: 1 G: 0 H: 0
复制插入
最后我们取出F,输出F,并将G的入度减1,得到新的入度表:
复制插入
A: -1 B: 0 C: 0 D: 0 E: 0 F: 0 G: 0 H: 0
复制插入
此时队列为空,排序完成。
拓扑排序是对有向无环图(DAG)的所有顶点进行排序的算法。在一个DAG图中,每个顶点代表一个任务或者一个活动,边表示一个任务必须在另一个任务之前完成。拓扑排序可以确定一个任务的执行顺序,从而使得每个任务在它的依赖任务之后执行。它是一种基于图的贪心算法,是解决排课、编译顺序等问题的常用算法。
现在我们看一道题目:
给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。
若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)。
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 −1。
数据范围
1≤n,m≤10^5
输入样例
3 3
1 2
2 3
1 3
输出样例
1 2 3
具体代码如下
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int N=100010;
int n,m;
int q[N],d[N];//d[N]表示入度为0的点
int h[N],e[N],ne[N],idx;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int topsort()
{
int hh=0,tt=-1;
for(int i=1;i<=n;i++)//遍历一遍顶点的入度
{
if(!d[i])//如果入度为 0, 则可以入队列
{
q[++tt]=i;
}
}
while(hh<=tt)
{
int t=q[hh++];
for(int i=h[t];i!=-1;i=ne[i])//循环删除 a 发出的边
{
int j=e[i];//a 有一条边指向b
d[j]--;//删除边后,b的入度减1
if(d[j]==0)//如果b的入度减为 0,则 b 可以输出,入队列
{
q[++tt]=j;
}
}
}
return tt==n-1;//如果队列中的点的个数与图中点的个数不相同,则可以进行拓扑排序
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
}
if(topsort())
{
for(int i=0;i<n;i++) printf("%d ",q[i]);
puts("");
}
else puts("-1");
return 0;
}
我们先用链表来储存这个图,且初始化距离都为-1
啥是拓扑排序?
一个有向图,如果图中有入度为 0 的点,就把这个点删掉,同时也删掉这个点所连的边。
一直进行上面出处理,如果所有点都能被删掉,则这个图可以进行拓扑排序。