53、【图】有向图中的拓扑序列——BFS(C/C++版)

题目描述

给定一个 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≤105

输入样例:

3 3
1 2
2 3
1 3

输出样例:

1 2 3

前置知识

42、【链表】静态单链表(C/C++版)

拓扑序列

拓扑排序

题目分析

拓扑序列的特征:
由一个图中的各点构成一个线性序列,若图中存在边<a, b>,则构成的序列中a一定在b的前面。

在构建拓扑序列过程中,每次将图中的结点添加到序列中时需要该结点的入度为0才可被添加进来。由此可知,存在环的图一定不能构成拓扑序列。而有向无环图一定可以构成拓扑序列。

因此,根据此规则,可选用BFS算法,来保证拓扑序列中结点间的先后次序进行实现。每次将入度为0的加入到队列中,并将该店所指向的下一个结点的入度减一。这一过程需要使用一个数组d[N]进行记录。
在这里插入图片描述
当所有的序列均可以按照规则放入到队列时,则该图可进行拓扑排序构成拓扑序列,反之则不能。

算法实现

#include <stdio.h>

int n, m;                   // 点数n和边数m
const int N = 1e5 + 10;
int h[N], e[N], ne[N], idx; // 构建邻接矩阵
int q[N], d[N];             // 队列q和记录入度数组d

void add(int a, int b){     // 头插法
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

bool topsort(){
    int front = 0, rear = 0;
    for(int i = 1; i <= n; i++)             // 从编号1开始将所有入度为0的点入队
        if(!d[i])       q[rear++] = i;      
    
    while(front < rear){
        int x = q[front++];
        for(int i = h[x]; i != -1; i = ne[i]){      // 获取入度为0的点的下一个邻接点
            int j = e[i];                           
            d[j]--;                                 // “删除”结点x后,与之相邻的结点j入度减一
            if(!d[j])       q[rear++] = j;      // 当结点j入度为0时,将其入队
        }        
    }
    return rear == n;       // 当所有序列均入队时,说明是按照拓扑排序入队,反之则不为拓扑序列
}


int main(){
    scanf("%d%d", &n, &m);
    for(int i = 0; i <= n; i++)      h[i] = -1;     // 初始化邻接矩阵中的顶点表指向“空指针”
    while(m--){
        int a, b;       scanf("%d%d", &a, &b);
        add(a, b);
        d[b]++;         // 结点b的入度加一
    }
    if(topsort()){      // 若为拓扑排序,则输出拓扑序列
        for(int i = 0; i < n; i++)		printf("%d ", q[i]);
        puts("");
    }else
        puts("-1");
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰阳星宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值