拓扑排序
简介
拓扑排序是用于处理有向无环图的一种排序算法。在这类图中,每个节点可以表示为一个任务,而每条有向边表示任务间的先决条件关系。拓扑排序的目标是生成一个线性序列,该序列满足图中所有有向边的前后关系。简而言之,如果图中从节点 A 指向节点 B,那么在拓扑排序的结果中,A 应该出现在 B 之前。
实例
848. 有向图的拓扑序列 - AcWing题库
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
int n, m;
#define N 100010
int e[2 * N], ne[2 * N], h[N], idx, d[N];
int queue[N], head = 0, tail = 0;
void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
bool topsort(){
for(int i = 1; i <= n; i++){
if(d[i] == 0){
queue[tail++] = i;
}
}
while(head < tail){
int temp = queue[head++];
for(int i = h[temp]; i != -1; i = ne[i]){
int j = e[i];
d[j]--;
if(d[j] == 0){
queue[tail++] = j;
}
}
}
return tail == n;
}
int main()
{
scanf("%d %d", &n, &m);
memset(h, -1, sizeof(h));
for(int i = 0; i < m; i++){
int x, y;
scanf("%d %d", &x, &y);
add(x, y);
d[y]++;
}
if(topsort()){
for(int i = 0; i < tail; i++){
printf("%d ", queue[i]);
}
}else{
printf("-1");
}
return 0;
}
这段代码是一个使用邻接表和队列实现的拓扑排序的完整示例,主要用于处理有向无环图。代码包含了图的构建、拓扑排序的执行,并在主函数中处理输入输出。
预处理部分和全局变量定义
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
int n, m;
#define N 100010
int e[2 * N], ne[2 * N], h[N], idx, d[N];
int queue[N], head = 0, tail = 0;
- n和m分别表示图中的节点数和边数。
- N是预定义的常量,表示节点的最大数量。
- e 和 ne 是数组,用于存储图的边信息。e[i] 表示边的目标节点,ne[i] 存储与当前节点相连的下一条边的索引。
- h数组用于存储每个节点的边的开始部分,即每个节点的邻接表的头部。
- idx 是用于填充 e和 ne 数组的当前索引。
- d数组用于存储每个节点的入度。
- queue 用于实现拓扑排序的队列,head 和 tail 分别是队列的头部和尾部索引。
辅助函数:添加边
void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
- 这个函数用于向图中添加一条从 a 指向 b的边。
- e[idx]设置为目标节点 b。
- ne[idx]连接到目前 a节点的边链表的头部。
- 更新 h[a] 为新的边的索引,然后 idx自增以用于下一次添加。
拓扑排序函数
bool topsort(){
for(int i = 1; i <= n; i++){
if(d[i] == 0){
queue[tail++] = i;
}
}
while(head < tail){
int temp = queue[head++];
for(int i = h[temp]; i != -1; i = ne[i]){
int j = e[i];
d[j]--;
if(d[j] == 0){
queue[tail++] = j;
}
}
}
return tail == n;
}
- 这个函数对图进行拓扑排序。
- 首先将所有入度为零的节点加入队列。
- 使用一个 while 循环来处理队列中的节点。对每个节点,遍历其所有的出边,减少相连节点的入度。如果某个节点的入度变为零,则将其加入队列。
- 函数返回一个布尔值,表示是否所有节点都已经被处理过,即队列大小是否等于节点数,这也检测了是否有环存在。
主函数
int main()
{
scanf("%d %d", &n, &m);
memset(h, -1, sizeof(h));
for(int i = 0; i < m; i++){
int x, y;
scanf("%d %d", &x, &y);
add(x, y);
d[y]++;
}
if(topsort()){
for(int i = 0; i < tail; i++){
printf("%d ", queue[i]);
}
}else{
printf("-1");
}
return 0;
}
- 主函数处理输入,初始化图的邻接表。
- 通过循环读入所有边,并更新相应节点的入度。
- 调用
topsort()
进行拓扑排序,并根据其返回值打印排序结果或-1
(表示图中存在环,无法进行拓扑排序)。