拓补排序是对有向图的一种处理方式,目的是得到拓补序列,一个有向图,他肯定有很多节点和有向边,拓补序列的性质就是图中所有的边所对应的两个点在该序列中都是起点在前终点在后
如下图,其中的一种拓补序列就是1 2 4 3 5,图中的所有有向边是{1,2},{1,4},{2,3},{2,5},{4,5}。1在2的前面,1在4的前面.......
那我们如何得到这个序列呢?其中图有一个概念在离散数学中提到过了————出度和入度,如果这个点作为有向边的起点,那么出度+1,如果作为终点,那么入度+1。首先,如果一个点入度为0,那么很明显它可以直接拿出来了,因为他不会作为有向边的终点了,那么以他为起点的边也可以删除,如下图(1这个点入度为0,可以直接拿出来,那么他与2 4相连的边也就可以直接删除了)
此时很好发现,2和4的入度也都-1了,且2和4入度都变为0了,那么我们也可以直接拿出来,此时得到的拓补序列就是1 2 4了。
此时就剩下3 5两个点并且边都已经被删除了,那么我们也可以直接拿出来了。所以我们得到了最后的拓补序列1 2 4 3 5。
以下就是代码实现了
点击查看代码
vector<int> L;//L用来存储最终的拓补序列
vector<int> g[10000];//vector用来存边g[i][j]就是以i为起点j为终点存在一条边
void tuobu()
{
deque<int> q;//q用来存储已知入度为0的点
for (int i = 1; i <= n; i++)//找到一开始入度就为0的点,比如上图的1点
{
if (depth[i] == 0) // 如果这个点入度为0
{
q.push_back(i);
L.push_back(i);
}
}
while (!q.empty())
{
int pos = q.front();//取出入度为0的点
q.pop_front();在序列中将他弹出(删除)
for (auto i : g[pos])//遍历以这个点为起点的边
{
depth[i]--;//把所有边的终点入度-1(模拟删除边,好比上图我取出1,那么2和4与1相连的边就要删除
//2和4的入度就要-1
if (depth[i] == 0) // 如果这个点入度为0,如果此时2 4两点的入度变成了0,那么就可以推入L和q
{
q.push_back(i);
L.push_back(i);
}
}
}
}