C++详细讲解图的拓扑排序

 更新时间:2022年05月30日 11:20:08   作者:quicklsleap  
拓扑排序(Topological Sorting)若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列

一、前言

且该序列必须满足下面两个条件:

  1. 每个顶点出现且只出现一次。
  2. 若存在一条从顶点 x到顶点 y的路径,那么在序列中顶点 x 出现在顶点 y的前面。

拓扑排序只适用于 AOV网 (有向无环图)

若图中有环,则一定不存在拓扑序。

可以证明,一个有向无环图,一定存在一个拓扑序列。有向无环图,又被称为拓扑图。

入度: 即有多少条边指向自己这个节点。

出度: 即有多少条边从自己这个节点指出去。

二、算法流程

算法流程:

用队列来执行 ,初始化所有入度为0的顶点入队。

主要由以下两步循环执行,直到不存在入度为 0 的顶点为止

选择一个入度为 0 的顶点,并将它输出;

删除图中从顶点连出的所有边

循环结束

若输出的顶点数小于图中的顶点数,则表示该图存在回路,即无法拓扑排序,

否则,输出的就是拓扑序列 (不唯一)

模板如下:

1.数组模拟队列实现拓扑排序

bool topsort()
{
    int hh = 0, tt = -1;
    // in[i] 存储点i的入度
    for (int i = 1; i <= n; i ++ )// 将所有入度为0的点加入队列
        if (in[i]==0)
            top[ ++ tt] = i;
    while (hh <= tt)
    {
        int t = top[hh ++ ];//找到入度为0的队头
  //遍历一下以t为头节点的的单链表,给每一个结点都要减去1,并再次找到入度为0的点
        for (int i = h[t]; i != -1; i = ne[i])
        {
        // 遍历 t 点的出边
            int j = e[i];
            if (-- in[j] == 0)//将入度减1,如果 j 入度为0,加入队列当中
                top[ ++ tt] = j;
        }
    }
    // 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。
    return tt == n - 1;
}

2.使用STL queue实现拓扑排序

bool topsort(){
    queue<int> q;
    int t;
    for(int i = 1;i <= n; ++i)// 将所有入度为0的点加入队列
        if(in[i] == 0) q.push(i);
    while(q.size()){
        t = q.front();//每次取出队列的首部
        top[cnt] = t;//加入到 拓扑序列中
        cnt ++; // 序列中的元素 ++
        q.pop();
        for(int i = h[t];i != -1; i = ne[i]){
            // 遍历 t 点的出边
            int j = e[i];
            in[j] --;// j 的入度 --
            if(in[j] == 0) q.push(j); //如果 j 入度为0,加入队列当中
        }
    }
    if(cnt < n) return 0;
    else return 1;
}

时间复杂度 O(n+m), n表示点数,m表示边数

三、有向图的拓扑排序

给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。

请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。

思路

我们每次找到入读为0的点,然后把他插入到队列里,然后将这个点删除,这也就意味着这个点连接的下一个点(可能是多个)的入度就会减1。

这个时候,我们就进入了下一轮。

我们因为前面将一个点删除了,那么它指向的点的入度就会都减去1,所以,就会出现新的点的入度为0,这个点显然是因为它的入度小,所以它理所应当的排在拓扑序里面在第二位。当前面的一个点没有了被删除了,那它就要首当其冲了。

和图的BFS思路很像,但是加了搜索的规则(即入度为零的先被搜索)可以看点这里

AC代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
int e[N],ne[N],h[N],idx,in[N],n,m,top[N],cnt = 1;
// e,ne,h,idx 邻接表模板
// in 代表每个元素的入度
// top是拓扑排序的序列,cnt代表top中有多少个元素
void add(int a,int b){
    e[idx] = b; ne[idx] = h[a];h[a] = idx ++;
}
bool topsort(){
    queue<int> q;
    int t;
    for(int i = 1;i <= n; ++i)// 将所有入度为0的点加入队列
        if(in[i] == 0) q.push(i);
    while(q.size()){
        t = q.front();//每次取出队列的首部
        top[cnt] = t;//加入到 拓扑序列中
        cnt ++; // 序列中的元素 ++
        q.pop();
        for(int i = h[t];i != -1; i = ne[i]){
            // 遍历 t 点的出边
            int j = e[i];
            in[j] --;// j 的入度 --
            if(in[j] == 0) q.push(j); //如果 j 入度为0,加入队列当中
        }
    }
    if(cnt < n) return 0;
    else return 1;
}
int main(){
    int a,b;
    cin >> n >> m;
    memset(h,-1,sizeof h);//给头节点赋值为-1;
    while(m--){
        cin >> a >> b;
        add(a,b);
        in[b] ++;// a -> b , b的入度++
    }
    if(topsort() == 0) cout << "-1";
    else {
        for(int i = 1;i <= n; ++i){
            cout << top[i] <<" ";
        }
    }
    return 0;
}

到此这篇关于C++详细讲解图的拓扑排序的文章就介绍到这了,更多相关C++拓扑排序内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解如何用C++写一个日期计算器

    详解如何用C++写一个日期计算器

    写一个日期计算器是一个很有现实意义的,比如,我想弄一个倒计时或者你女朋友问你和她认识多少天了,这就需要让两个日期相减,又或者和别人约定几年以后怎么怎么样,这就得知道那一天的日期,接下来小编借给大家介绍一下如何用C++写一个日期计算器,感兴趣的朋友可以动手尝试
    2024-04-04
  • 利用C语言编写“剪刀石头布”小游戏

    利用C语言编写“剪刀石头布”小游戏

    这篇文章主要给大家介绍了关于如何利用C语言编写“剪刀石头布”小游戏的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • C语言中动态内存管理初学者容易犯的6个错误分享

    C语言中动态内存管理初学者容易犯的6个错误分享

    本篇文章主要介绍了初学者使用C语言中动态内存管理的4个函数时最容易犯的6个错误,以及如何避免这些错误,文中的示例代码讲解详细,感兴趣的可以了解一下
    2023-04-04
  • 详解进程同步与互斥机制

    详解进程同步与互斥机制

    进程同步是一个操作系统级别的概念,是在多道程序的环境下,存在着不同的制约关系,为了协调这种互相制约的关系,实现资源共享和进程协作,从而避免进程之间的冲突,引入了进程同步
    2021-06-06
  • 详解VSCode下C++环境配置过程

    详解VSCode下C++环境配置过程

    这篇文章主要介绍了VSCode C++环境配置过程,在这大家需要在代码的目录下的.vscode文件夹下创建launch.json、tasks.json,具体实现过程跟随小编一起看看吧
    2021-11-11
  • C++ boost scoped_ptr智能指针详解

    C++ boost scoped_ptr智能指针详解

    智能指针是一种像指针的C++对象,但它能够在对象不使用的时候自己销毁掉。虽然STL提供了auto_ptr,但是由于不能同容器一起使用(不支持拷贝和赋值操作),因此很少有人使用。它是Boost各组件中,应用最为广泛的一个
    2022-11-11
  • C语言数据结构之简易计算器

    C语言数据结构之简易计算器

    这篇文章主要为大家详细介绍了C语言数据结构之简易计算器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • C++基于reactor的服务器百万并发实现与讲解

    C++基于reactor的服务器百万并发实现与讲解

    这篇文章主要介绍了C++基于reactor的服务器百万并发实现与讲解,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • C语言数据结构 link 链表反转的实现

    C语言数据结构 link 链表反转的实现

    这篇文章主要介绍了C语言数据结构 link 链表反转的实现的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下
    2017-09-09
  • 如何在Qt中实现关于Json 的操作

    如何在Qt中实现关于Json 的操作

    JSON是一种轻量级数据交换格式,常用于客户端和服务端的数据交互,不依赖于编程语言,在很多编程语言中都可以使用JSON,这篇文章主要介绍了在Qt中实现关于Json的操作,需要的朋友可以参考下
    2023-08-08

最新评论