搜索与图论-有向图的拓扑序列

简介: 搜索与图论-有向图的拓扑序列

文章目录



一、有向图的拓扑序列

  • 有向图的拓扑序列就是图的广度优先遍历的一个应用。

1. 拓扑序列


若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。(起点在终点的前面)

拓扑序列是针对有向图,无向图是没有拓扑序列的。

有向无环图一定是拓扑序列,有向有环图一定不是拓扑序列。

例如下图,由于 c 指向了 a ,所以该图不是拓扑序列。

db4097606a674e81986d4656015955df.png

  • 同样的例子,由于 d 指向了 b ,所以该图也不是拓扑序列。

b4111362f43a4b19b5899e90867b36cb.png


2. 拓扑排序


  • 入度是指被其他点指向的数量。
  • 出度是指指向其他点的数量。
  • 举例说明:

f329ce7659564a51b4b6626e117be9d7.png

  • 由上图可知,a 指向 b 和 c ,b 被 a 指向,并且指向 c ,c 被 a 和 b 指向。
  • 因此,a、b、c 的入度和出度分别为:
节点 入度 出度
a 0 2
b 1 1
c 2 0


因此,所有入度为 0 的点都可以作为起点。


3. 如何进行拓扑排序


  • 一个有向图,如果图中有入度为 0 的点,就把这个点删掉,同时也删掉这个点所连的边。
  • 一直进行上面处理,如果所有点都能被删掉,则这个图可以进行拓扑排序。
  • 一个拓扑序列可以有多种输出方式。
  • 举例说明:

d7e762f5e0f3455ea2a5c04c357ed476.png


  • 开始时,图是这样的状态,发现 A 的入度为 0,所以删除 A 和 A 上所连的边,结果如下图:


b07be1383afd4c84883a7e76aa343945.png


这时发现 B 的入度为 0,C 的入度为 0,所以删除 B 和 B 上所连的边、 C 和 C 上所连的边,结果如下图:

4c034ff9f0b64aba9d908e40c14953fd.png


这时发现 D 的入度为 0,所以删除 D 和 D 上所连的边(如果有就删),结果如下图:

01d1b3bf79424a5190f9b657127c5728.png


  • 这时整个图被删除干净,所有能进行拓扑排序。
  • 对上述过程的实现,可以通过 queue 队列实现,入队的点的顺序就是拓扑序列


4. 拓扑排序具体实现详见例题有向图的拓扑序列

二、有向图的拓扑序列例题——有向图的拓扑序列

题目描述


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


输入样例

3 3

1 2

2 3

1 3

输出样例

1 2 3


具体实现

1. 样例演示


  • 输入 n=3 和 m=3 。表示一个有 3 个点,3 条边。
  • 从 1 指向 2 。
  • 从 2 指向 3 。
  • 从 1 指向 3 。
  • 如下图所示。


7a2ab0f31e6d41b98eb2bd0e84b682ad.png


2. 实现思路


  • 首先记录各个点的入度。
  • 然后将入度为 0 的点放入队列。
  • 将队列里的点依次出队列,然后找出所有出队列这个点发出的边,删除边,同时边的另一侧的点的入度 -1。
  • 如果所有点都进过队列,则可以拓扑排序,输出所有顶点。否则输出 -1,代表不可以进行拓扑排序。



3. 代码注解


int h[N], e[N], ne[N], idx;表示邻接表的存储方式。

int d[N];表示点的入度。

int q[N];表示队列。

if(d[j]==0);如果点 j 的入度为零了,就将点 j 入队。

return tt==n-1;表示如果 n 个点都入队了话,那么该图为拓扑图,返回 true ,否则返回 false 。

其他注解在实现代码当中体现。


4. 实现代码

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx;  
int d[N];
int q[N];
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx;
    idx ++;
}
//返回布尔序列是否存在, 若存在,则存储在q数组中
bool topsort()
{
    int hh = 0;
    int tt = -1;
    //遍历每一个节点, 入度为零则入队
    for (int i = 1; i <= n; i ++ )
    {
        if (!d[i])
        {
            tt ++;
            q[tt] = i;
        }
    }
    while (hh <= tt)
    {
        //队列不空,则取出头节点
        int t = q[hh];
        hh ++;
        //遍历头节点的每一个出边
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (-- d[j] == 0)
            {
                tt ++; 
                q[tt] = j;
            }
        }
    }
    return tt == n - 1;
}
int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i ++ )
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
        d[b] ++ ;
    }
    if (!topsort()) 
    {
        puts("-1");
    }
    else
    {
        for (int i = 0; i < n; i ++ )
        {
           cout << q[i] << " ";
        }
        puts("");
    }
    system("pause");
    return 0;
}







相关文章
|
3天前
|
算法 测试技术 C#
【树 图论 阶乘 组合 深度优先搜索】1916. 统计为蚁群构筑房间的不同顺序
【树 图论 阶乘 组合 深度优先搜索】1916. 统计为蚁群构筑房间的不同顺序
|
11天前
|
算法 搜索推荐 Java
图搜索算法详解
图搜索算法是用于在图结构中寻找特定节点或路径的算法。图是由节点(或顶点)和边组成的集合,节点代表对象,边代表节点之间的连接。图搜索算法广泛应用于各种领域,比如网络路由、社交媒体分析、推荐系统等。 V哥最近总是在多个地方都看到关于图搜索算法的讨论
|
1月前
|
人工智能 算法 BI
【图论】【 割边】【C++算法】1192. 查找集群内的关键连接
【图论】【 割边】【C++算法】1192. 查找集群内的关键连接
|
7月前
|
算法 测试技术 C++
C++算法:图中的最短环
C++算法:图中的最短环
|
11月前
|
算法 UED
【算法入门&搜索法】走迷宫|单源最短路径1
【算法入门&搜索法】走迷宫|单源最短路径1
136 0
|
11月前
|
存储 算法 搜索推荐
拓扑排序:求取拓扑序列
拓扑排序简单讲就是在可求拓扑序列的有向无回路图(有向无环图)中求取拓扑序列的排序算法。通俗讲就是按活动的先后次序进行排序的序列,并且每一个顶点只出现一次,它可以表述出完成某一项活动所需要的前置活动
55 0
拓扑排序:求取拓扑序列
|
12月前
|
存储 索引
搜索与图论-树与图的广度优先遍历
搜索与图论-树与图的广度优先遍历
|
12月前
|
存储 索引
搜索与图论-树与图的深度优先遍历
搜索与图论-树与图的深度优先遍历
|
12月前
|
机器学习/深度学习 算法 C++
搜索与图论- Dijkstra 算法
搜索与图论- Dijkstra 算法
|
12月前
|
机器学习/深度学习 存储 算法
搜索与图论 - floyd 算法
搜索与图论 - floyd 算法