有向无环图的拓扑排序

拓扑排序

简介

拓扑排序是用于处理有向无环图的一种排序算法。在这类图中,每个节点可以表示为一个任务,而每条有向边表示任务间的先决条件关系。拓扑排序的目标是生成一个线性序列,该序列满足图中所有有向边的前后关系。简而言之,如果图中从节点 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(表示图中存在环,无法进行拓扑排序)。
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值