【算法学习笔记】19:拓扑排序

2023-11-14

1 简述

计算拓扑序列的一个方式是,用BFS来尝试访问所有的节点,但是有一个约束就是只有入度为 0 0 0的节点才能被加入到扩展队列里。每次从队列里取出一个节点,也就同时在图中将这个节点拆除,所以它的所有后继的节点都减少 1 1 1,如果已经减少到 0 0 0,那么就可以加入到队列中。

在这里插入图片描述

在上面的例子中,一开始只有 a a a的入度是 0 0 0,所以先把 a a a加入到队列中,队列中:
a a a

然后取出队头 a a a并在图中拆除,然后它的后继 c c c的入度变成 1 1 1 b b b的入度变成 0 0 0,把 b b b加入到队列里,队列中:
b b b

然后取出队头 b b b并在图中拆除,然后它的后继 c c c d d d的入度都变成 0 0 0,都加入到队列里,队列中是:
c   d c~d c d

或者
d   c d~c d c

接下来也是重复这个过程,最后得到拓扑序列是 a   b   c   d a~b~c~d a b c d或者 a   b   d   c a~b~d~c a b d c(取决于 c c c d d d哪个先从“ d d d的所有后继”这个集合中访问)。

2 模板题:有向图的拓扑序列

在实现拓扑排序时,用模拟队列来代替STL的队列比较方便。一方面是,观察上面的过程可以发现,只要所有节点都被加入到队列里了,那么就能说明所有的节点都能被访问,就说明存在拓扑序列。如果用模拟队列,由于它的两个指针一直往后移的特性,只要看一下队尾指针tt是不是在n-1位置就能知道是不是这n个元素都加入过队列中了。

另外,由于模拟队列删除元素只是做++hh的操作,而没有破坏队头hh位置的元素取值,所以最后输出拓扑序列的时候可以直接从0n-1遍历一下队列,输出的就是拓扑序了。

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e5 + 10;

int n, m;

// 记录每个节点的入度
int d[N];

// 邻接表
int h[N], e[N], ne[N], idx;

// 邻接表添加有向边
void add_edge(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx ++ ;
}

// 模拟队列
int q[N], hh, tt = -1;

// 拓扑排序,如果存在拓扑序返回真
bool topo_sort() {
    // 先将所有入度为0的节点入队
    for (int i = 1; i <= n; i ++ ) {
        if (!d[i])
            q[ ++ tt] = i;
    }
    // BFS访问过程
    while (hh <= tt) {
        // 取队头节点t
        int t = q[hh ++ ];
        // 删除节点t,所以将节点t的所有后继j的入度-1
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            d[j] -- ;
            // 如果已经减少到0了,就加到队列里
            if (!d[j]) q[ ++ tt] = j;
        }
    }
    // 如果所有n个节点都加入过队列,就存在拓扑序
    return tt == n - 1;
}

int main() {
    // 初始化邻接表
    memset(h, -1, sizeof h);
    // 读取m条有向边,同时维护节点的入度
    cin >> n >> m;
    for (int i = 0; i < m; i ++ ) {
        int a, b;
        cin >> a >> b;
        add_edge(a, b);
        d[b] ++ ;
    }
    // 如果存在拓扑序,模拟队列中依次入队的顺序就是拓扑序
    if (topo_sort()) {
        for (int i = 0; i < n; i ++ )
            cout << q[i] << ' ';
        cout << endl;
    }
    else {
        cout << -1 << endl;
    }
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【算法学习笔记】19:拓扑排序 的相关文章

  • 图论:Dijkstra算法——最详细的分析,图文并茂,一次看懂!

    文章目录 1 Dijkstra算法简介 2 算法实现范例 3 邻接矩阵 4 Dijkstra 算法的 C 描述 5 Dijkstra 算法的 Matlab 描述 6 温故知新 1 Dijkstra算法简介 背景 迪杰斯特拉算法 Dijkst
  • UVA-1601 万圣节后的早晨 题解答案代码 算法竞赛入门经典第二版

    GitHub jzplp aoapc UVA Answer 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 以三个点的当前位置作为状态 广度优先遍历 找到终点即为最短次数 注意 一次可以移动多个点 但是每个点只能移动一步 在同一次中 B可
  • hdu 2586 How far away ?

    Problem acm hdu edu cn showproblem php pid 2586 Meaning 给一棵 n 个点的树 和 n 1 条边的边权 多次询问树上两点的距离 Analysis 以任意顶点为根 DFS 预处理出所有结点
  • P1218 [USACO1.5]特殊的质数肋骨 Superprime Rib【普及】

    USACO1 5 特殊的质数肋骨 Superprime Rib 题目描述 农民约翰的母牛总是产生最好的肋骨 你能通过农民约翰和美国农业部标记在每根肋骨上的数字认出它们 农民约翰确定他卖给买方的是真正的质数肋骨 是因为从右边开始切下肋骨 每次
  • 无向图的遍历-BFS与DFS

    一 理论部分 无向图的遍历可用深度搜索 DFS 与广度搜索 BFS 深度搜索的基本方式是由图的一个节点1出发然后随机选一个与其相邻的节点2 接着在选择一个与其相邻的节点3 当一条路遍历完后再选择最近一个遍历过的 且相邻节点有未遍历过的节点
  • 西安电子科技大学第二届程序设计新生赛-F-zxy的长跑【欧拉回路】

    题目链接 好极了的欧拉回路 我们想知道怎样才能跑完所有的边 我们可以从度为奇数的点出发 因为这是欧拉回路的无向图的先觉调节 当然 这道题还有另外一种可能就是这是一个环 1 gt 2 2 gt 3 3 gt 4 4 gt 1 那么就没有奇数度
  • Codeforces-1454E Number of Simple Paths(基环树-思维)

    题目大意 给你n个点 n条边 求图中简单路径的个数 题目思路 n个点n条边 那么图中一定有一个环 拿这个图来讲 我们将两点间的关系分为4种 1 两点都在环上 简单路径的个数为2 例如2与5 2 一个点在环上一个点不在环上 简单路径个数为2
  • 大学生团体天梯赛(第十届)

    题目地址 天梯赛 include
  • 【01规划】POJ 3621 Sightseeing Cows

    POJ 3621 Sightseeing Cows 题意 给定一张 n 个点 m 条边的有向图 每个点都有一个权值 f i 每条边都有一个权值 t i 求图中的一个环 使 环上各点的权值之和 除以 环上各边的权值之和 最大 输出这个最大值
  • 深度、广度优先搜索

    文章目录 二 图的遍历 2 1 深度优先搜索 DFS DFS森林 应用 2 2 广度优先搜索 BFS 基本操作 应用 二 图的遍历 2 1 深度优先搜索 DFS DFS森林 Vertextype GetVex ALGraph G int v
  • 矩阵树定理

    启蒙 http zhengruioi com contest 1416 T1 T2的10分暴力 后面是论文科技 不搞了 https www luogu com cn problem P6178 O n 3
  • [UVA1364

    评测地址 网址1 网址2 题目描述 题意 给出n位骑士 然后有m个关系 每个关系以格式 a b a b a b给出 表达骑士 a a
  • 《算法图解》读书笔记(二)

    第六章 图 广度优先搜索 1 解决最短路径问题 shortest path problem 的算法被称为广度优先搜索 breadth first search 2 图由节点 node 和边 edge 组成 一个节点可能与众多节点直接相连 这
  • Codeforces Round #751 (Div. 2) D. Frog Traveler(BFS)

    题解 因为我们最多把所有的点跳一遍么 所以直接BFS模拟一下就行了 注意现在跳的点不能是以前已经跳过的点 并且只能越跳越高 否则没有意义 这样就保证了时间复杂度是线性的 AC代码 include
  • Summer Holiday HDU - 1827 强连通分量+缩点

    To see a World in a Grain of Sand And a Heaven in a Wild Flower Hold Infinity in the palm of your hand And Eternity in a
  • LeetCode——1302. 层数最深叶子节点的和

    题目描述 给你一棵二叉树的根节点 root 请你返回层数最深的叶子节点的和 示例 1 输入 root 1 2 3 4 5 null 6 7 null null null null 8 输出 15 示例 2 输入 root 6 7 8 2 7
  • 图论算法<三>:判断有向图中是否有存在循环 ,以及环的个数和各个环中的元素

    1 目的 判断有向图中是否有存在循环 以及环的个数和各个环中的元素 2 示例效果 2 1 原始数据 路线起终点整理如下 共计12个顶点 19条边 起点 终点 1 最后的1代表起点终点是连通的 起点 终点 1 2 4 1 起点 终点 1 9
  • 数据结构算法—邻接表存储的无向图求连通分量个数

    数据结构算法 邻接表存储的无向图求连通分量个数 邻接表存储结构 typedef struct ArcNode int adjvex 边指向的顶点 struct ArcNode nextarc 下一条边的指针 ArcNode typedef
  • 图论--最近公共祖先LCA

    最近公共祖先LCA LCA Least Common Ancestors 即最近公共祖先 是指这样一个问题 在有根树中 找出某两个结点u和v最近的公共祖先 另一种说法 离树根最远的公共祖先 最近公共祖先是相对于两个节点来说的 一般来说 最近
  • 数据结构--树存储结构 & 深度优先遍历 & 广度优先遍历 通俗易懂

    树的概念 首先 树是一种常用的非线性数据结构 是以边 Edge 相连的节点 Node 的集合 每个节点存储对应的值 当存在子节点时与之相连 根节点 是树的首个节点 边 所有节点都由边相连 用于标识节点间的关系 叶子结点 树的末端节点 它们没

随机推荐

  • mysql数据库入门教程

    Markdown database notebook Markdown database notebook 1 1 Mysql知识 基础 1 1 1 Msyql的基本知识 1 2 Mysql知识 深入 1 2 1 Mysql的储存引擎 1
  • DIV与Table布局在大型网站的可用性比较

    DIV与TABLE本身并不存在什么优缺点 所谓web标准只是推荐的是正确的使用标签 好比说 DIV用于布局 而TABLE则本来就是转二维数据的 让TABLE做该做的事 并不是说页面里不出现TABLE就是多么多么牛 用DIV进行排版的优势就是
  • jQuery-migrate 插件---各类版本下载

    步骤 1 CDN jquery migrate 2 找到所需版本打开 3 全选复制到自己创建的记事本 4 复制 5 粘贴到IntelliJ IDEA 模块下的 webapp js 没有自己手动创建目录 jquery migrate 1 4
  • Oracal的Lpad函数

    lpad函数是Oracle数据库函数 lpad函数从左边对字符串使用指定的字符进行填充 从其字面意思也可以理解 l是left的简写 pad是填充的意思 所以lpad就是从左边填充的意思 语法格式如下 lpad string padded l
  • unity笔记-20161109

    1 Animator CullingMode 动画器剔除模式 AlwaysAnimate Always animate the entire character Object is animated even when offscreen
  • 在Python中如何优雅地处理PDF文件

    1 引言 PDF文档是我们在日常工作中经常会遇到的文件格式 有时我们需要编辑并从中提取一些有用的数据 在本文中 我将向大家介绍如何使用Python中的PDF库从PDF文档中提取文本 表格和图像以及其他类型的数据 闲话少说 我们直接开始吧 2
  • JS实现请假时长计算(计算小时数差)

    给公司做了一套系统 涉及到请假单功能开发 在计算请假时长这块总结一下 按天计算的就不总结了比较简单 这里总结一下按小时数计算的 话不多说 直接上代码 获取两个日期相差的工作小时 不包括节假日 function getHour StartTi
  • Python 中的异常种类

    常用异常 AttributeError 试图访问一个对象没有的树形 比如foo x 但是foo没有属性x IOError 输入 输出异常 基本上是无法打开文件 ImportError 无法引入模块或包 基本上是路径问题或名称错误 Inden
  • Matlab quiver函数用法 - 画矢量箭头图

    提要 quiver x y u v 在点 x y 处画 u v 所定义的向量箭头 x y u v必须是维度和元素数都一样的矩阵 如果是一维数组的话 x y u v的元素数必须一致 quiver函数会自动调整箭头的长度以适应显示 quiver
  • iOS静态方式绕过svc反动态调试

    在iOS反动态调试中 常用到 svc 0x80 通过svc汇编实现对ptrace syscall的调用 实现反动态调试 使得lldb无法附加到app进程 不易定位到代码位置 增加反调试绕过难度 如何绕过这种反调试手段呢 本文通过搜索app的
  • ajax请求发送成功,后端没有响应

    前端请求状态200 但是后端无反应结果是以为我的登录拦截器把这个请求拦截了 登录之后就发现后端有响应了 2021 4 1日常错误
  • Python自学笔记2-语法

    这里介绍Python的基本语法和编程风格 Python的保留字 如下表 不能以这些名字给函数或变量命名 and exec not assert finally or break for pass class from print conti
  • UE5 C++插件开发指南目录

    这一篇原本的标题是 如何将插件上架到UE虚幻商城 但是Up主聆枫LingFeng已经分享了相关议题 而且非常详细 UE 虚幻商城上架指南 所以这一篇就改写目录了 其实由谁来讲并不重要 重要的是讲的内容是否是读者需要的 希望大家可以从中受益
  • SQL练习(less-5\8)延时注入

    本文为学习笔记 仅限学习交流 不得利用 从事危害国家或人民安全 荣誉和利益等活动 SQL注入 字符型 延时注入 延时型语句 sleep 参数 任意正整数 一般为秒 If a b c 它的意思就是如果条件A成立 则输出结果B 否则输出结果C
  • HTML 好看界面

    无聊逛外网的时候 突然看见一个用HTML写的界面 我觉得挺好看 对于我这个才接触这个的学生来说 挺厉害的 所以我也把他分享出来 你们可以去参考参考
  • 第50讲:Scrapy 部署不用愁,Scrapyd 的原理和使用

    上节课我们的分布式爬虫部署完成并可以成功运行了 但是有个环节非常烦琐 那就是代码部署 我们设想下面的几个场景 如果采用上传文件的方式部署代码 我们首先需要将代码压缩 然后采用 SFTP 或 FTP 的方式将文件上传到服务器 之后再连接服务器
  • linux磁盘分区以及配置文件设置

    硬盘分区有三种 主磁盘分区 83 扩展磁盘分区 5 逻辑分区 包括swap交换分区82 一个硬盘主分区至少有1个 最多4个 扩展分区可以没有 最多1个 且主分区 扩展分区总共不能超过4个 逻辑分区可以有若干个 交换分区必须存在但一般不用 补
  • hdu 6121 Build a tree

    Problem acm hdu edu cn showproblem php pid 6121 Meaning 一棵 n 个点的完全 k 叉树 结点标号从 0 到 n 1 求以每一棵子树的大小的异或和 Analysis 一层层地统计答案 找
  • LED 数码管共阴共阳的区别+静态/动态显示

    51单片机 数码管动态显示 1 共阴共阳定义 LED 共阴极指的是LED共同的接点是GND 接地 而共阳极指的是LED共同的接点是电源 LED亮灯的条件是两端有电势差 最后一段h dp小数点在高位 第一段a在低位 hgfedcba xxxx
  • 【算法学习笔记】19:拓扑排序

    1 简述 计算拓扑序列的一个方式是 用BFS来尝试访问所有的节点 但是有一个约束就是只有入度为 0 0 0的节点才能被加入到扩展队列里 每次从队列里取出一个节点 也就同时在图中将这个节点拆除 所以它的所有后继的节点都减少 1 1 1 如果已