拓扑排序的C++实现

2023-05-16


tags: C++ DSA Sort GT

写在前面

写一下有向无环图(DAG, Directed Acyclic Graph)上的拓扑排序, 废话不多说了, 介绍部分大家可以参考算法导论或者 oi-wiki.

https://oi-wiki.org/graph/topo/;

我写的完整代码:
Topological-Sort

我们可以拿大学选课的例子来描述这个过程,比如学习大学课程中有:单变量微积分,线性代数,离散数学概述,概率论与统计学概述,语言基础,算法导论,机器学习。当我们想要学习 算法导论 的时候,就必须先学会 离散数学概述 和 概率论与统计学概述,不然在课堂就会听的一脸懵逼。当然还有一个更加前的课程 单变量微积分。这些课程就相当于几个顶点 , 顶点之间的有向边 就相当于学习课程的顺序。显然拓扑排序不是那么的麻烦,不然你是如何选出合适的学习顺序。下面将介绍如何将这个过程抽象出来,用算法来实现。

但是如果某一天排课的老师打瞌睡了,说想要学习 算法导论,还得先学 机器学习,而 机器学习 的前置课程又是 算法导论,然后你就一万脸懵逼了,我到底应该先学哪一个?当然我们在这里不考虑什么同时学几个课程的情况。在这里,算法导论 和 机器学习 间就出现了一个环,显然你现在没办法弄清楚你需要学什么了,于是你也没办法进行拓扑排序了。因而如果有向图中存在环路,那么我们就没办法进行 拓扑排序 了。

思路

有两种实现方法, 一种是基于 BFS 的, 叫做 Kahn 算法, 本质上就是广度优先搜索, 统计入度为 0 的所有节点, 放入结果集合, 同时将入度为 1 的放入结果集合, 最后判断结果集大小是不是结点个数(这样才说明图上每一个节点都遍历到了, 也即图是 DAG).

还有一种是基于深度优先搜索的, 在 DFS 的时候, 遍历直到节点没有出度(即到达函数结尾, return 之前), 此时的节点就是"末尾"节点, 也就是说一条深搜路径的结尾, 这时候放入栈中, 结果集就是栈的出栈序列.

实现: BFS

bool topoSort_Kahn() {
    vector<int> L;
    queue<int> S;
    // 计算节点入度
    for (auto v : G)
        for (int i : v) ++in[i];
    // 存入度为 0 的节点
    for (int i{}; i < n; ++i)
        if (in[i] == 0) S.push(i); 
    // BFS
    while (!S.empty()) {
        int u = S.front();
        S.pop();
        L.push_back(u);
        for (auto v : G[u]) {
            if (--in[v] == 0) S.push(v);
        }
    }
    if (L.size() == n) {
        cout << "排序结果:\n" << L;
        return true;
    }
    return false;
}

实现: DFS

void topoSort_DFS() {
    //
    bool visited[n];
    memset(visited, false, sizeof(visited));
    stack<int> st;

    function<void(int)> dfs = [&](int v) {
        visited[v] = true;
        for (auto i : G[v])
            if (!visited[i]) dfs(i);
        st.emplace(v);
    };
    for (int i{}; i < n; ++i)
        if (!visited[i]) dfs(i);
    cout << st;
}

代码示例

utility-function

#include <iostream>
#include <vector>
#include <queue>
#include <stack>
using namespace std;


ostream& operator<<(ostream& os, const vector<int>& v) {
    for (auto i : v) os << i << " ";
    return os << endl;
}
ostream& operator<<(ostream& os, queue<int> v) { // pass by value
    for (; !v.empty(); v.pop()) os << v.front() << " ";
    return os << endl;
}
ostream& operator<<(ostream& os, stack<int> v) { // pass by value
    for (; !v.empty(); v.pop()) os << v.top() << " ";
    return os << endl;
}

针对上面这个图, 采用不同的拓扑排序策略, 得到的排序结果不同:

// 图的例子: 参见
// https://oi-wiki.org/graph/images/topo-example.svg
// 邻接表
vector<vector<int>> G{
    {1, 5, 6}, {},  {0, 3},       {5}, {},   {4}, {4, 9},
    {6},       {7}, {10, 11, 12}, {},  {12}, {},
};

int n = G.size(); // n: vertex
// int m{15};        // m: edge
// 存储结点的入度
vector<int> in(n);
// 1, 1, 0, 1, 2, 2, 2, 1, 0, 1, 1, 1, 2

int main() {
    // topoSort_Kahn(); // BFS
    // 2 8 0 3 7 1 5 6 4 9 10 11 12
    topoSort_DFS();
    // 8 7 2 3 0 6 9 11 12 10 5 4 1
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

拓扑排序的C++实现 的相关文章

随机推荐

  • 基于arm架构的ubuntu18 .04安装Anaconda3 + pytorch+python3.9

    记录一下项目踩坑经历 xff08 查了很多资料 xff0c 感觉都是对有基础的人来说的 xff0c 对于刚接触深度学习环境的小白并不友好 xff0c 很多细节并没有 xff0c 各种坑无数 xff0c 我也是花了好长时间才弄清楚 xff09
  • MathType7应用中文版特色功能介绍

    MathType 是由美国Design Science公司开发的功能强大的数学公式编辑器 xff0c 它同时支持Windows和Macintosh 操作系统 xff0c 与常见的文字处理软件和演示程序配合使用 xff0c 能够在各种文档中加
  • QT的延时函数

    延时函数在收发数据的时候用处很大 在其他方面也有用处 这里提供四种方法 1 多线程程序 使用QThread sleep 或者QThread msleep 或QThread usleep 或QThread wait 进行延时处理 Sleep不
  • ios基础篇(八)—— iOS触摸事件

    iOS中的事件 iOS事件中分为三大类 xff0c 触摸事件 xff0c 加速器事件 xff0c 远程控制事件 响应者对象 在iOS 中 不是任何对象都是能处理事件的 xff0c 只有继承于UIResponder 的对象才能接受并且处理事件
  • 牛客C++ACM模式输入输出11道题分析与总结

    tags C 43 43 Interview 写在前面 感觉好久没写博客了 最近看的书多 但是真正沉淀下来的东西却很少 这次总结一下C 43 43 刷题中常用的一些IO操作 也就是ACM模式中的一些基本操作 看到知识星球里面推荐了牛客的一个
  • C++类内初始化vector的一个小坑与分析解决

    tags C 43 43 Debug STL 问题 先来看这样一份代码 vector span class token operator lt span vector span class token operator lt span sp
  • 力扣螺旋矩阵系列总结(C++)

    tags DSA C 43 43 LeetCode Python 写在前面 螺旋矩阵系列 严格来说不算双指针 但是其中蕴含的思想很像双指针 应该叫四指针 54 螺旋矩阵 力扣 xff08 LeetCode xff09 需要四个指针分别在需要
  • GitHub提交时出现Host key verification failed无法读取远程仓库的解决方案

    tags Git Debug Tips 问题 今天提交代码时候发现有这样一个问题 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 6
  • 在M1芯片MacBook上部署微软最新Visual-GPT的完整方案总结

    tags Python MacOS 写在前面 好久没写博客 因为最近一直忙着看Effective系列 终于告一段落了 看到微软出的Visual chatgpt 想试试 后来失败了 在这里记录一下吧 参考 https github com m
  • Andrews定理的证明(对称单峰多项式乘积保持对称单峰性)

    tags Maths 预备定义 原始文献A Theorem on Reciprocal Polynomials with Applications to Permutations and Compositions 对称多项式 对称 reci
  • 双向链表的增删改查C++完整实现

    tags C 43 43 DSA LinkedList 写在前面 写一下双向链表的增删改查 用C 43 43 实现 完整代码可以看我的GitHub 节点类 链表类 节点 span class token keyword class span
  • 单向环形链表的增删改查C++完整实现

    tags C 43 43 DSA 写在前面 刚写了双向链表的 趁热打铁再来一个环形链表的 这次就有点复杂了 但是还是可以接受的 实现环形链表的关键就是不能通过判断是否遍历到空节点来结束循环 这会导致死循环 只能用指针是否遍历回到头结点来判断
  • FL水果编曲20.8中文版下载 flstudio语言修改中文教程

    FL Studio中文版一般又称水果音乐制作 水果音乐软件手机版可以记录 xff0c 序列编辑 混合和渲染完成的歌曲等 FL Studio xff08 水果音乐制作 xff09 软件含43种虚拟音源 可同时录制64轨音频轨 增强音频编辑与后
  • 双向环形链表的C++增删改查完整实现

    tags C 43 43 DSA 写在前面 最后写一下双向循环链表吧 跟前面的没啥太大区别 注意取余操作以及循环跳出的条件 代码 GitHub 节点类 链表类 节点类 和双向链表一模一样 span class token keyword c
  • 牛客网ACM模式输入输出11道题目的C++解答(C标准IO版)

    tags C 43 43 Interview 写在前面 之前写过关于牛客网的输入输出的题目 但是是用C 43 43 的标准IO写的 虽然方便 但是据说速度会很慢 这里还是再用C重写一遍 主要用到了scanf和printf 地址 牛客竞赛 A
  • 面试题: C++类内静态成员必须在类外初始化吗? --分析与示例

    tags C 43 43 OOP 写在前面 最近看到了这样一个题 静态数据成员定义之后 xff0c 必须在类外进行初始化 看完了Effective系列之后 我会给出答案 错误 为什么呢 下面来深入分析一下 非常量静态数据成员 看下面这个例子
  • C++字符串+和push_back创建字符串的性能比较

    tags C 43 43 String 写在前面 刷力扣 415 字符串相加 时候发现这样一个现象 使用 s1 span class token operator 61 span span class token generic funct
  • C++并发编程实战笔记(一)线程概念与基本控制

    tags C 43 43 Concurrency 写在前面 在C 43 43 中实现多线程还是很容易的 不像C的pthreads接口 下面来总结一下C 43 43 多线程的一些基本操作 包括线程的创建 合并 分离 获取ID等操作 主要参考了
  • 差分数组C++实现与力扣题目总结

    tags DSA C 43 43 LeetCode 写在前面 总结一下经典的差分数组方法 华为机试刚考了 思路很简单 但是没遇到的话想写出来还是有点难度的 参考了 labuladong 的博客 里面的代码是 Java 实现的 这里用 C 4
  • 拓扑排序的C++实现

    tags C 43 43 DSA Sort GT 写在前面 写一下有向无环图 DAG Directed Acyclic Graph 上的拓扑排序 废话不多说了 介绍部分大家可以参考算法导论或者 oi wiki https oi wiki o