首页/新闻资讯/正文详情

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

发布时间:2026/11/2 14:56:12 来源:云帆数科 栏目:资讯中心
C++ 有向图拓扑排序算法
代码#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::setDAGNodeK, V* in; std::setDAGNodeK, 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::functionvoid(const K k, const V v) f, bool start_from_head true); void WalkHeads(std::functionvoid(const K k, const V v) f); void WalkTails(std::functionvoid(const K k, const V v) f); std::unordered_setK NextKeys(); std::unordered_setK NextKeys(const K key); private: bool IsCyclic(const DAGNodeK, V from, const DAGNodeK, V to) const; void RefreshWalkSequences(); std::vectorstd::setK ConnectedComponents() const; void DFS(const K k, std::unordered_setK* visited, std::setK* connected_components) const; std::vectorK TopologicalSequence(const std::setK connected_components, bool start_from_head) const; private: std::mapK, DAGNodeK, V bucket_; std::unordered_setK heads_; std::unordered_setK tails_; std::vectorstd::vectorK sequences_start_from_head_; std::vectorstd::vectorK sequences_start_from_tail_; private: bool allow_modify_ true; std::vectorstd::vectorK sequences_start_from_head_for_next_; std::unordered_setK current_heads_for_next_; }; template typename K, typename V inline bool DAGGraphK, 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 DAGGraphK, 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 DAGGraphK, V::Exist(const K key) const { return bucket_.count(key); } template typename K, typename V inline void DAGGraphK, 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 DAGGraphK, V::Size() const { return bucket_.size(); } template typename K, typename V inline void DAGGraphK, V::Walk(std::functionvoid(const K k, const V v) f, bool start_from_head) { if (sequences_start_from_head_.empty()) { RefreshWalkSequences(); } const std::vectorstd::vectorK seqs_to_walk start_from_head ? sequences_start_from_head_ : sequences_start_from_tail_; for (const std::vectorK seq : seqs_to_walk) { std::for_each(std::begin(seq), std::end(seq), [](const K key) { const DAGNodeK, V node bucket_.at(key); f(node.k, node.v); }); } } template typename K, typename V inline void DAGGraphK, V::WalkHeads( std::functionvoid(const K k, const V v) f) { if (sequences_start_from_head_.empty()) { RefreshWalkSequences(); } for (const std::vectorK seq : sequences_start_from_head_) { std::for_each(std::begin(seq), std::end(seq), [](const K key) { if (heads_.count(key)) { const DAGNodeK, V node bucket_.at(key); f(node.k, node.v); } }); } } template typename K, typename V inline void DAGGraphK, V::WalkTails( std::functionvoid(const K k, const V v) f) { if (sequences_start_from_head_.empty()) { RefreshWalkSequences(); } for (const std::vectorK seq : sequences_start_from_tail_) { std::for_each(std::begin(seq), std::end(seq), [](const K key) { if (tails_.count(key)) { const DAGNodeK, V node bucket_.at(key); f(node.k, node.v); } }); } } template typename K, typename V inline std::unordered_setK DAGGraphK, 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_setK DAGGraphK, 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_setK res; for (std::vectorK 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::setDAGNodeK, V* nodes bucket_.at(key).out; for (DAGNodeK, V* v : nodes) { const std::setDAGNodeK, V* prev_nodes v-in; bool no_prev_node_in_seq std::all_of(std::begin(prev_nodes), std::end(prev_nodes), [](DAGNodeK, 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 DAGGraphK, V::IsCyclic(const DAGNodeK, V from, const DAGNodeK, V to) const { std::queueDAGNodeK, V* q; for (DAGNodeK, V* v : from.in) { q.emplace(v); } std::unordered_setDAGNodeK, V* visited; while (!q.empty()) { DAGNodeK, V* node q.front(); q.pop(); if (visited.count(node)) { continue; } if (node to) { return true; } visited.emplace(node); for (DAGNodeK, V* v : node-in) { q.emplace(v); } } return false; } template typename K, typename V inline void DAGGraphK, V::RefreshWalkSequences() { sequences_start_from_head_.clear(); sequences_start_from_tail_.clear(); const std::vectorstd::setK connected_components ConnectedComponents(); for (const std::setK x : connected_components) { const std::vectorK seq_from_head TopologicalSequence(x, true); const std::vectorK 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::vectorstd::setK DAGGraphK, V::ConnectedComponents() const { std::vectorstd::setK res; std::unordered_setK visited; for (auto x : bucket_) { std::setK tmp; DFS(x.second.k, visited, tmp); if (!tmp.empty()) { res.emplace_back(tmp); } } std::sort(std::begin(res), std::end(res), [](const std::setK lhs, const std::setK rhs) { return lhs.size() rhs.size(); }); return res; } template typename K, typename V inline void DAGGraphK, V::DFS(const K k, std::unordered_setK* visited, std::setK* connected_components) const { if (visited-count(k)) { return; } visited-emplace(k); connected_components-emplace(k); if (!bucket_.at(k).in.empty()) { for (DAGNodeK, V* v : bucket_.at(k).in) { DFS(v-k, visited, connected_components); } } if (!bucket_.at(k).out.empty()) { for (DAGNodeK, V* v : bucket_.at(k).out) { DFS(v-k, visited, connected_components); } } } template typename K, typename V inline std::vectorK DAGGraphK, V::TopologicalSequence( const std::setK connected_components, bool start_from_head) const { std::mapK, std::vectorK adjacency_list; std::mapK, int32_t in_degree; for (const K key : connected_components) { if (!in_degree.count(key)) { in_degree.emplace(key, 0); } const std::setDAGNodeK, V* nodes start_from_head ? bucket_.at(key).out : bucket_.at(key).in; for (DAGNodeK, V* v : nodes) { adjacency_list[key].emplace_back(v-k); in_degree[v-k]; } } std::queueK q; for (auto x : in_degree) { if (x.second 0) { q.emplace(x.first); } } std::vectorK 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() { DAGGraphint, std::unique_ptrMockPipelineEngine 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::vectorint v; std::vectorint start_order{ 13, 6, 7, 8, 11, 12, 9, 10, 0, 1, 3, 2, 4, 5 }; std::vectorint start_order2{ 13, 6, 7, 8, 11, 12, 9, 10, 0, 3, 1, 4, 2, 5 }; d.Walk( [](int key, const std::unique_ptrMockPipelineEngine pipeline) { pipeline-Start(); v.emplace_back(key); }, start_from_head); assert(v start_order || v start_order2); } { std::vectorint v; std::vectorint stop_order{ 13, 7, 6, 10, 9, 8, 12, 11, 5, 2, 4, 1, 3, 0 }; std::vectorint stop_order2{ 13, 7, 6, 10, 9, 12, 8, 11, 5, 2, 4, 1, 3, 0 }; d.Walk( [](int key, const std::unique_ptrMockPipelineEngine pipeline) { pipeline-Stop(); v.emplace_back(key); }, !start_from_head); assert(v stop_order || v stop_order2 ); } { std::vectorint v; std::vectorint heads_order{ 13, 6, 8, 11, 0 }; d.WalkHeads( [](int key, const std::unique_ptrMockPipelineEngine pipeline) { pipeline-Destroy(); v.emplace_back(key); }); assert(v heads_order); } { std::vectorint v; std::vectorint tails_order{ 13, 7, 10, 5 }; d.WalkTails( [](int key, const std::unique_ptrMockPipelineEngine pipeline) { pipeline-Destroy(); v.emplace_back(key); }); assert(v tails_order); } { std::vectorint test_sequence{ 13, 6, 7, 0, 1, 3, 4, 2, 5, 8, 11, 12, 9, 10 }; std::unordered_setint heads{ 0, 6, 8, 11, 13 }; assert(d.NextKeys() heads); std::vectorstd::unordered_setint 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(); }效果

相关推荐

QT WIN11 FluentUI APP开发
QT WIN11 FluentUI APP开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 14:56:12

C++实现web token加密生成验证
C++实现web token加密生成验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 14:56:12

HarmonyOS介绍真题
HarmonyOS介绍真题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 14:56:12

AI日报解读:Agent工程化、大模型训练与AI视频生产落地
AI日报解读:Agent工程化、大模型训练与AI视频生产落地

今天这份日报,我重点想聊的是AI Agent、大模型训练以及AI视频生产这几条线。因为从今天的热词来看,大家已经不满足于“AI能干什么”,而是开始关心“AI怎么干得更稳、更便宜、更能落地”——比如Agent怎么扛并发、DeepSeek公开的智能体训练新方… · 2026/11/2 20:13:37

大模型入门到实战:系统学习路线、微调部署与RAG应用
大模型入门到实战:系统学习路线、微调部署与RAG应用

大模型这东西,这两年几乎是个人都在提,但真正想系统学进去的人,反而最容易迷路。网上的资料太碎了,今天看到一个讲Transformer的视频,明天刷到一篇讲微调的公众号,后天又有人告诉你必须学RAG,结… · 2026/11/2 20:13:37

量子神经网络架构与MindQuantum实践指南
量子神经网络架构与MindQuantum实践指南

1. 量子神经网络与学习机器的跨界融合查理芒格作为投资界的传奇人物,其"学习机器"理念强调通过跨学科知识体系的构建来提升认知能力。这种思想在量子计算领域同样具有深刻的启示意义。量子神经网络作为量子计算与经典神经网络结合的产物,其开发… · 2026/11/2 20:13:37

集成学习与概率校准:杯赛赛事预测及Streamlit应用
集成学习与概率校准:杯赛赛事预测及Streamlit应用

两年前帮一个做体育数据分析的小团队重构他们的预测脚本,对方一开始的要求很朴素:把历史比赛数据喂给模型,算出每场比赛的胜负概率,再套个网页界面能点就行。我当时图省事,直接用一个梯度提升模型单打独斗,… · 2026/11/2 20:13:37

大数据建模可视化工具与实战技巧全解析
大数据建模可视化工具与实战技巧全解析

1. 大数据领域数据建模可视化展示的核心价值在大数据项目实践中,数据建模是构建数据仓库和分析系统的基石。我曾参与过多个PB级数据平台的建设,深刻体会到:优秀的可视化展示能让抽象的数据模型变得直观可理解,这对团队协作和模型优… · 2026/11/2 20:13:37

Apollo人形机器人核心技术解析与量产突破
Apollo人形机器人核心技术解析与量产突破

1. 项目概述:Apollo人形机器人融资里程碑解析2026年2月,机器人行业迎来标志性事件——Apptronik公司宣布完成5.2亿美元A-X轮融资,使其A轮总融资额突破9.35亿美元,累计融资逼近10亿美元大关。这笔资金将直接用于Apollo人形机器人的… · 2026/11/2 20:12:36

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18

雷达信号调制技术解析:从LFM到伪线性调频原理与应用
雷达信号调制技术解析:从LFM到伪线性调频原理与应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15

基于MAX14431与TM4C123的工业SPI隔离电路设计全攻略
基于MAX14431与TM4C123的工业SPI隔离电路设计全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15

PLFM_RADAR:相位线性调频雷达信号处理框架
PLFM_RADAR:相位线性调频雷达信号处理框架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码