C++ 有向图拓扑排序算法

news2024/11/15 13:52:45
代码 
#include <algorithm>
#include <cassert>
#include <functional>
#include <map>
#include <memory>
#include <queue>
#include <set>
#include <unordered_set>
#include <vector>

namespace jc {

    template <typename K, typename V>
    struct DAGNode {
        K k;
        V v;
        std::set<DAGNode<K, V>*> in;
        std::set<DAGNode<K, V>*> out;
    };

    template <typename K, typename V>
    class DAGGraph {
    public:
        bool AddEdge(const K& from, const K& to);

        V& operator[](const K& key);

        bool Exist(const K& key) const;

        void Clear();

        std::size_t Size() const;

        void Walk(std::function<void(const K& k, const V& v)> f,
            bool start_from_head = true);

        void WalkHeads(std::function<void(const K& k, const V& v)> f);

        void WalkTails(std::function<void(const K& k, const V& v)> f);

        std::unordered_set<K> NextKeys();

        std::unordered_set<K> NextKeys(const K& key);

    private:
        bool IsCyclic(const DAGNode<K, V>& from, const DAGNode<K, V>& to) const;

        void RefreshWalkSequences();

        std::vector<std::set<K>> ConnectedComponents() const;

        void DFS(const K& k, std::unordered_set<K>* visited,
            std::set<K>* connected_components) const;

        std::vector<K> TopologicalSequence(const std::set<K>& connected_components,
            bool start_from_head) const;

    private:
        std::map<K, DAGNode<K, V>> bucket_;
        std::unordered_set<K> heads_;
        std::unordered_set<K> tails_;
        std::vector<std::vector<K>> sequences_start_from_head_;
        std::vector<std::vector<K>> sequences_start_from_tail_;

    private:
        bool allow_modify_ = true;
        std::vector<std::vector<K>> sequences_start_from_head_for_next_;
        std::unordered_set<K> current_heads_for_next_;
    };

    template <typename K, typename V>
    inline bool DAGGraph<K, V>::AddEdge(const K& from, const K& to) {
        assert(allow_modify_);
        if (from == to || !bucket_.count(from) || !bucket_.count(to) ||
            IsCyclic(bucket_.at(from), bucket_.at(to))) {
            return false;
        }
        bucket_.at(from).out.emplace(&bucket_.at(to));
        bucket_.at(to).in.emplace(&bucket_.at(from));
        heads_.erase(to);
        tails_.erase(from);
        sequences_start_from_head_.clear();
        sequences_start_from_tail_.clear();
        return true;
    }

    template <typename K, typename V>
    inline V& DAGGraph<K, V>::operator[](const K& key) {
        if (!bucket_.count(key)) {
            assert(allow_modify_);
            bucket_[key].k = key;
            heads_.emplace(key);
            tails_.emplace(key);
            sequences_start_from_head_.clear();
            sequences_start_from_tail_.clear();
        }
        return bucket_.at(key).v;
    }

    template <typename K, typename V>
    inline bool DAGGraph<K, V>::Exist(const K& key) const {
        return bucket_.count(key);
    }

    template <typename K, typename V>
    inline void DAGGraph<K, V>::Clear() {
        allow_modify_ = true;
        bucket_.clear();
        heads_.clear();
        tails_.clear();
        sequences_start_from_head_.clear();
        sequences_start_from_tail_.clear();
    }

    template <typename K, typename V>
    inline std::size_t DAGGraph<K, V>::Size() const {
        return bucket_.size();
    }

    template <typename K, typename V>
    inline void DAGGraph<K, V>::Walk(std::function<void(const K& k, const V& v)> f,
        bool start_from_head) {
        if (sequences_start_from_head_.empty()) {
            RefreshWalkSequences();
        }
        const std::vector<std::vector<K>>& seqs_to_walk =
            start_from_head ? sequences_start_from_head_ : sequences_start_from_tail_;
        for (const std::vector<K>& seq : seqs_to_walk) {
            std::for_each(std::begin(seq), std::end(seq), [&](const K& key) {
                const DAGNode<K, V>& node = bucket_.at(key);
                f(node.k, node.v);
                });
        }
    }

    template <typename K, typename V>
    inline void DAGGraph<K, V>::WalkHeads(
        std::function<void(const K& k, const V& v)> f) {
        if (sequences_start_from_head_.empty()) {
            RefreshWalkSequences();
        }
        for (const std::vector<K>& seq : sequences_start_from_head_) {
            std::for_each(std::begin(seq), std::end(seq), [&](const K& key) {
                if (heads_.count(key)) {
                    const DAGNode<K, V>& node = bucket_.at(key);
                    f(node.k, node.v);
                }
                });
        }
    }

    template <typename K, typename V>
    inline void DAGGraph<K, V>::WalkTails(
        std::function<void(const K& k, const V& v)> f) {
        if (sequences_start_from_head_.empty()) {
            RefreshWalkSequences();
        }
        for (const std::vector<K>& seq : sequences_start_from_tail_) {
            std::for_each(std::begin(seq), std::end(seq), [&](const K& key) {
                if (tails_.count(key)) {
                    const DAGNode<K, V>& node = bucket_.at(key);
                    f(node.k, node.v);
                }
                });
        }
    }

    template <typename K, typename V>
    inline std::unordered_set<K> DAGGraph<K, V>::NextKeys() {
        assert(allow_modify_);  // allowed call once unless Clear()
        allow_modify_ = false;
        current_heads_for_next_ = heads_;
        if (sequences_start_from_head_.empty()) {
            RefreshWalkSequences();
        }
        return heads_;
    }

    template <typename K, typename V>
    inline std::unordered_set<K> DAGGraph<K, V>::NextKeys(const K& key) {
        assert(!allow_modify_);  // must call NextKeys() before
        assert(current_heads_for_next_.count(key));
        current_heads_for_next_.erase(key);

        std::unordered_set<K> res;
        for (std::vector<K>& seq : sequences_start_from_head_for_next_) {
            auto it = std::find(begin(seq), std::end(seq), key);
            if (it == std::end(seq)) {
                continue;
            }
            seq.erase(it);
            const std::set<DAGNode<K, V>*>& nodes = bucket_.at(key).out;
            for (DAGNode<K, V>* v : nodes) {
                const std::set<DAGNode<K, V>*>& prev_nodes = v->in;
                bool no_prev_node_in_seq =
                    std::all_of(std::begin(prev_nodes), std::end(prev_nodes),
                        [&](DAGNode<K, V>* in_node) {
                            return std::find(std::begin(seq), std::end(seq),
                                in_node->k) == std::end(seq);
                        });
                if (no_prev_node_in_seq) {
                    current_heads_for_next_.emplace(v->k);
                    res.emplace(v->k);
                }
            }
            break;
        }
        return res;
    }

    template <typename K, typename V>
    inline bool DAGGraph<K, V>::IsCyclic(const DAGNode<K, V>& from,
        const DAGNode<K, V>& to) const {
        std::queue<DAGNode<K, V>*> q;
        for (DAGNode<K, V>* v : from.in) {
            q.emplace(v);
        }

        std::unordered_set<DAGNode<K, V>*> visited;
        while (!q.empty()) {
            DAGNode<K, V>* node = q.front();
            q.pop();
            if (visited.count(node)) {
                continue;
            }
            if (node == &to) {
                return true;
            }
            visited.emplace(node);
            for (DAGNode<K, V>* v : node->in) {
                q.emplace(v);
            }
        }

        return false;
    }

    template <typename K, typename V>
    inline void DAGGraph<K, V>::RefreshWalkSequences() {
        sequences_start_from_head_.clear();
        sequences_start_from_tail_.clear();

        const std::vector<std::set<K>> connected_components = ConnectedComponents();
        for (const std::set<K>& x : connected_components) {
            const std::vector<K> seq_from_head = TopologicalSequence(x, true);
            const std::vector<K> seq_from_tail = TopologicalSequence(x, false);
            assert(!seq_from_head.empty());
            assert(!seq_from_tail.empty());
            sequences_start_from_head_.emplace_back(seq_from_head);
            sequences_start_from_tail_.emplace_back(seq_from_tail);
        }

        sequences_start_from_head_for_next_ = sequences_start_from_head_;
    }

    template <typename K, typename V>
    inline std::vector<std::set<K>> DAGGraph<K, V>::ConnectedComponents() const {
        std::vector<std::set<K>> res;
        std::unordered_set<K> visited;
        for (auto& x : bucket_) {
            std::set<K> tmp;
            DFS(x.second.k, &visited, &tmp);
            if (!tmp.empty()) {
                res.emplace_back(tmp);
            }
        }
        std::sort(std::begin(res), std::end(res),
            [&](const std::set<K>& lhs, const std::set<K>& rhs) {
                return lhs.size() < rhs.size();
            });
        return res;
    }

    template <typename K, typename V>
    inline void DAGGraph<K, V>::DFS(const K& k, std::unordered_set<K>* visited,
        std::set<K>* connected_components) const {
        if (visited->count(k)) {
            return;
        }
        visited->emplace(k);
        connected_components->emplace(k);
        if (!bucket_.at(k).in.empty()) {
            for (DAGNode<K, V>* v : bucket_.at(k).in) {
                DFS(v->k, visited, connected_components);
            }
        }
        if (!bucket_.at(k).out.empty()) {
            for (DAGNode<K, V>* v : bucket_.at(k).out) {
                DFS(v->k, visited, connected_components);
            }
        }
    }

    template <typename K, typename V>
    inline std::vector<K> DAGGraph<K, V>::TopologicalSequence(
        const std::set<K>& connected_components, bool start_from_head) const {
        std::map<K, std::vector<K>> adjacency_list;
        std::map<K, int32_t> in_degree;

        for (const K& key : connected_components) {
            if (!in_degree.count(key)) {
                in_degree.emplace(key, 0);
            }
            const std::set<DAGNode<K, V>*>& nodes =
                start_from_head ? bucket_.at(key).out : bucket_.at(key).in;
            for (DAGNode<K, V>* v : nodes) {
                adjacency_list[key].emplace_back(v->k);
                ++in_degree[v->k];
            }
        }

        std::queue<K> q;
        for (auto& x : in_degree) {
            if (x.second == 0) {
                q.emplace(x.first);
            }
        }

        std::vector<K> res;
        while (!q.empty()) {
            const K key = q.front();
            q.pop();
            res.emplace_back(key);
            for (const K& k : adjacency_list[key]) {
                if (--in_degree.at(k) == 0) {
                    q.emplace(k);
                }
            }
        }

        assert(res.size() == connected_components.size());  // graph is DAG
        return res;
    }

}  // namespace jc

namespace jc::test {

    class MockPipelineEngine {
    public:
        void Start() {}
        void Stop() {}
        void Destroy() {}
    };

    void test() {
        DAGGraph<int, std::unique_ptr<MockPipelineEngine>> d;
        // Make Direct Acyclic Graph:
        //    0    6      11  13
        //   / \   |      |
        //  1   3  7  8   12
        //  | x |      \ /
        //  2   4       9
        //   \ /        |
        //    5         10
        // Traverse each child graph in order whose size smaller

        // Start Order:
        // 13
        // 6 -> 7
        // 8 -> 11 -> 12 -> 9 -> 10
        // 0 -> 1 -> 3 -> 2 -> 4 -> 5
        // Stop Order:
        // 13
        // 7 -> 6
        // 10 -> 9 -> 8 -> 12 -> 11
        // 5 -> 2 -> 4 -> 1 -> 3 -> 0

        constexpr int nodes_count = 14;
        for (int i = 0; i < nodes_count; ++i) {
            d[i].reset(new MockPipelineEngine);
        }
        assert(d.AddEdge(0, 1));
        assert(d.AddEdge(0, 3));
        assert(d.AddEdge(1, 2));
        assert(d.AddEdge(3, 4));
        assert(d.AddEdge(1, 4));
        assert(d.AddEdge(3, 2));
        assert(d.AddEdge(2, 5));
        assert(d.AddEdge(4, 5));
        assert(d.AddEdge(6, 7));
        assert(d.AddEdge(8, 9));
        assert(d.AddEdge(9, 10));
        assert(d.AddEdge(11, 12));
        assert(d.AddEdge(12, 9));

        assert(d.Size() == nodes_count);

        for (int i = 0; i < nodes_count; ++i) {
            assert(d.Exist(i));
        }

        assert(!d.AddEdge(1, 0));
        assert(!d.AddEdge(2, 0));
        assert(!d.AddEdge(4, 0));
        assert(!d.AddEdge(7, 6));
        assert(!d.AddEdge(10, 11));
        assert(!d.AddEdge(13, 13));
        assert(!d.AddEdge(13, 14));

        constexpr bool start_from_head = true;
        {
            std::vector<int> v;
            std::vector<int> start_order{ 13, 6, 7, 8, 11, 12, 9, 10, 0, 1, 3, 2, 4, 5 };
            std::vector<int> start_order2{ 13, 6, 7, 8, 11, 12, 9, 10, 0, 3, 1, 4, 2, 5 };
            d.Walk(
                [&](int key, const std::unique_ptr<MockPipelineEngine>& pipeline) {
                    pipeline->Start();
                    v.emplace_back(key);
                },
                start_from_head);
            assert(v == start_order || v == start_order2);
        }

        {
            std::vector<int> v;
            std::vector<int> stop_order{ 13, 7, 6, 10, 9, 8, 12, 11, 5, 2, 4, 1, 3, 0 };
            std::vector<int> stop_order2{ 13, 7, 6, 10, 9, 12, 8, 11, 5, 2, 4, 1, 3, 0 };
            d.Walk(
                [&](int key, const std::unique_ptr<MockPipelineEngine>& pipeline) {
                    pipeline->Stop();
                    v.emplace_back(key);
                },
                !start_from_head);
            assert(v == stop_order || v == stop_order2
            );
        }

        {
            std::vector<int> v;
            std::vector<int> heads_order{ 13, 6, 8, 11, 0 };
            d.WalkHeads(
                [&](int key, const std::unique_ptr<MockPipelineEngine>& pipeline) {
                    pipeline->Destroy();
                    v.emplace_back(key);
                });
            assert(v == heads_order);
        }

        {
            std::vector<int> v;
            std::vector<int> tails_order{ 13, 7, 10, 5 };
            d.WalkTails(
                [&](int key, const std::unique_ptr<MockPipelineEngine>& pipeline) {
                    pipeline->Destroy();
                    v.emplace_back(key);
                });
            assert(v == tails_order);
        }

        {
            std::vector<int> test_sequence{ 13, 6, 7, 0,  1,  3, 4,
                                           2,  5, 8, 11, 12, 9, 10 };

            std::unordered_set<int> heads{ 0, 6, 8, 11, 13 };
            assert(d.NextKeys() == heads);

            std::vector<std::unordered_set<int>> next_keys{
                {}, {7}, {}, {1, 3}, {}, {2, 4}, {}, {5}, {}, {}, {12}, {9}, {10}, {},
            };

            assert(test_sequence.size() == nodes_count);
            assert(next_keys.size() == nodes_count);
            for (int i = 0; i < nodes_count; ++i) {
                assert(d.NextKeys(test_sequence[i]) == next_keys[i]);
            }
        }

        d.Clear();
        assert(d.Size() == 0);
        for (int i = 0; i < nodes_count; ++i) {
            assert(!d.Exist(i));
        }
    }

}  // namespace jc::test

//int main() { jc::test::test(); }
效果

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2071377.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JAVA基础】位运算

文章目录 位运算按位与操作按位或操作按位取反按位亦或 移位运算有符号左移有符号右移 位运算 处理数据的时候可以直接对组成整形数值的各个位完成操作 &|~^andornotxor 下面我们以byte类型为例子&#xff1a; 按位与操作 两个操作数&#xff0c;如果同为1则为1&#…

【JavaEE初阶】IP协议

目录 &#x1f4d5;引言 &#x1f334;IP协议的概念 &#x1f333;IP数据报 &#x1f6a9;IPv4协议头格式 &#x1f6a9;IPv6的诞生 &#x1f3c0;拓展 &#x1f384;IP地址 &#x1f6a9;IP地址的格式&#xff1a; &#x1f6a9;IP地址的分类 &#x1f3c0;网段划分…

【计算机三级-数据库技术】操作题大题(第七套)

第七套操作题 第46题 假定要建立一个关于篮球职业联盟的数据库&#xff0c;需管理如下信息&#xff1a; 每个球队有球队名称、所在城市&#xff1b; 每位球员有球员姓名、薪酬; 每场比赛有比赛编号、比赛时间、比赛结果、参加比赛的主场球队、参加比赛的客场球队。 其中带下划…

Redis—基础篇

Redis基础 1. Redis 简介2. Redis 应用3. Redis 数据结构3.1 String3.2 hash3.3 list3.4 set3.5 sorted set 4. Redis 为什么快&#xff1f;5. Redis I/O 多路复用6. Redis 6.0多线程 1. Redis 简介 Redis 是一种基于键值对的 NoSQL 数据库 Redis 中的 value 支持 string、ha…

关于jupyter notebook 的输出 (outputs )

jupyter notebook 的输出 (outputs )在元素达到一定的个数后&#xff0c;就会按一行一个元素进行展示&#xff0c;百来个还好&#xff0c;一旦过千&#xff0c;那滚轮势必撸冒烟&#xff0c;所以能不能解决呢&#xff1f; 先看个例子&#xff0c; 一个找质数、合数的函数 cal3&…

【Linux篇】vim编译器

1. 介绍 vi / vim是visual interface的简称&#xff0c;是Linux中最典型的文本编辑器。 同图形化界面中的文本编辑器一样&#xff0c;vi是命令行下对文本文件进行编辑的绝佳选择。 vim是vi的加强版本&#xff0c;兼容vi的所有指令&#xff0c;不仅能编译文本&#xff0c;而且…

排序补充之快排的三路划分法

排序补充之快排的三路划分法 快排性能的关键点分析&#xff1a; 决定快排性能的关键点是每次单趟排序后&#xff0c;key对数组的分割&#xff0c;如果每次选key基本⼆分居中&#xff0c;那么快 排的递归树就是颗均匀的满⼆叉树&#xff0c;性能最佳。但是实践中虽然不可能每次…

数学建模笔记(四):熵权

背景&基本思想介绍 在实际的评价类问题中&#xff0c;在前面所说的层次分析法以及Topsis法中&#xff0c;指标权重的确定往往是通过主观的评价得来的&#xff0c;如果在没有专家的情况下&#xff0c;我们自己的权重分配往往可能带有一定的主观性&#xff0c;有没有一种可以…

linux系统离线安装docker并配置阿里云镜像源

制作docker.service文件 创建docker.service文件 cd /etc/systemd/system/ touch docker.service编辑docker.service文件 vim docker.service// 注意&#xff0c;将其中的ip地址&#xff0c;改成您的服务器地址&#xff0c;其它参数不用改。 //--insecure-registry192.168.8…

代码随想录算法day22 | 回溯算法part04 | 491.递增子序列,46.全排列,47.全排列 II

491.递增子序列 本题和大家做过的 90.子集II 非常像&#xff0c;但又很不一样&#xff0c;很容易掉坑里。 力扣题目链接(opens new window) 给定一个整型数组, 你的任务是找到所有该数组的递增子序列&#xff0c;递增子序列的长度至少是2。 示例: 输入: [4, 6, 7, 7]输出: [[4,…

【学习笔记】时间序列模型(ARIMA)

文章目录 前言一、时间序列时间序列数据 二、ARIMA 模型大纲模型前提平稳性检验 差分整合移动平均自回归模型 ARIMA(p,q,d)自回归模型 (AR( p ))移动平均模型 (MA( q ))自回归移动平均模型(ARMA(p,q))差分自回归移动平均模型 ARIMA(p,d,q) 确定 p&#xff0c;q结果分析和模型检…

SpringBoot集成kafka-消费者批量消费消息

SpringBoot集成kafka-消费者批量消费消息 1、消费者2、生产者3、application.yml配置文件4、实体类5、生产者发送消息测试类6、测试6.1、测试启动生产者6.2、测试启动消费者 1、消费者 设置批量接收消息 package com.power.consumer;import org.apache.kafka.clients.consume…

IC-Light容器构建详细指南

一、介绍 IC-Light 是一个操纵图像照明的项目&#xff0c;能够让一张普普通通的照片焕发光彩。 IC-Light&#xff0c;全称为“Imposing Consistent Light”&#xff0c;是一款由 AI 图像处理专家张吕敏&#xff08;ControlNet 的作者&#xff09;精心开发的创新工具。主要用于…

启用 UFW 防火墙后如何打开 80 和 443 端口?

UFW&#xff08;Uncomplicated Firewall&#xff09;是一款 Linux 系统上用户友好的管理防火墙规则的工具。它简化了控制网络流量的过程&#xff0c;使用户更容易开放或阻止端口。 本文将引导您使用 UFW 打开端口 80 (HTTP) 和 443 (HTTPS) 端口。您将了解如何启用这些端口&am…

uni-app项目搭建和模块介绍

工具:HuilderX noed版本:node-v17.3.1 npm版本:8.3.0 淘宝镜像:https://registry.npmmirror.com/ 未安装nodejs可以进入这里https://blog.csdn.net/a1241436267/article/details/141326585?spm1001.2014.3001.5501 目录 1.项目搭建​编辑 2.项目结构 3.使用浏览器运行…

华为Cloud连接配置

Cloud(云)连接意思为本地电脑和eNSP中的虚拟的VRP系统连接的 配置Cloud 先添加UDP 再添加需要使用的网卡 网卡建议使用虚拟机的网卡&#xff0c;如果没有虚拟机也可以使用其他网卡&#xff0c;自己设定一下IP就行 端口映射设置 配置R1 [R1]int e0/0/0 [R1-Ethernet0/0/0]ip …

B. 不知道该叫啥

题意&#xff1a;求长度为n的数列方案数&#xff0c;数列需满足两个条件&#xff1a;1.均为正整数。2.相邻两个数乘积不能超过m 思路&#xff1a;考虑dp。 设表示前i个点以j结尾的方案数&#xff0c;则有&#xff1a; 可以得出&#xff1a; 双指针数论分块解决。把每个m/i相…

一个下载镜像非常快的网站--华为云

1、镜像的下载飞速 链接&#xff1a;mirrors.huaweicloud.com/ubuntu-releases/24.04/ 下载一个的ubuntu24.04的镜像文件&#xff0c;5.7G的大文件&#xff0c;不到1分钟就下完毕了&#xff0c; 比起阿里云下载的速度600K/S,这个速度是100多倍。 非常的神速&#xff0c;非常…

如何选择高品质科研实验室用太阳光模拟器

概述 太阳光模拟器是一种能够模拟太阳光照射条件的设备&#xff0c;主要用于实验室环境中对太阳能相关材料和设备进行性能测试。这类模拟器能够提供与自然太阳光谱相似的光照&#xff0c;同时还能精确控制光照强度和照射角度&#xff0c;以满足不同测试需求。 对于被归类为太…

Leetcode 104. 二叉树的最大深度 C++实现

Leetcode 104. 二叉树的最大深度 问题&#xff1a;给定一个二叉树root&#xff0c;返回其最大深度。 二叉树的最大深度是指从根节点到最远叶子节点的最长路径上的节点数。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* …