数据结构与算法之美:拓扑排序

         Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!

我的博客:<但凡.

我的专栏:《编程之路》、《数据结构与算法之美》、《C++修炼之路》、《Linux修炼:终端之内 洞悉真理》

感谢你打开这篇博客!希望这篇博客能为你带来帮助,也欢迎一起交流探讨,共同成长。

        拓扑排序其实是图相关的内容,但是当时我忘了这一部分了,所以单独拿一篇文章来补充上。

目录

1、拓扑排序概述

2、拓扑排序的实现 

基于邻接表的实现

Kahn算法实现(基于入度)

关键注意事项

3、应用场景扩展


 

1、拓扑排序概述

        拓扑排序是对有向无环图(DAG)的顶点进行线性排序,使得对于图中的每一条有向边 (u, v),顶点 u 在排序中总是位于顶点 v 的前面。常用于任务调度、依赖解析等场景。

        简单来说,拓扑排序就是每次从入度为0的点开始,每次都走入度为0的点,直到遍历完所有的顶点。拓扑排序只适用于有向无环图,并且,拓扑排序的结果可能不唯一。

2、拓扑排序的实现 

基于邻接表的实现

以下是使用邻接表和深度优先搜索(DFS)的C++实现:

#include <iostream>
#include <vector>
#include <stack>
#include <list>
using namespace std;class Graph {int V; // 顶点数list<int>* adj; // 邻接表void topologicalSortUtil(int v, bool visited[], stack<int>& Stack);public:Graph(int V);void addEdge(int v, int w);void topologicalSort();
};Graph::Graph(int V) {this->V = V;adj = new list<int>[V];
}void Graph::addEdge(int v, int w) {adj[v].push_back(w); // 添加边v->w
}void Graph::topologicalSortUtil(int v, bool visited[], stack<int>& Stack) {visited[v] = true;for (auto i = adj[v].begin(); i != adj[v].end(); ++i)if (!visited[*i])topologicalSortUtil(*i, visited, Stack);Stack.push(v);
}void Graph::topologicalSort() {stack<int> Stack;bool* visited = new bool[V];for (int i = 0; i < V; i++)visited[i] = false;for (int i = 0; i < V; i++)if (!visited[i])topologicalSortUtil(i, visited, Stack);while (!Stack.empty()) {cout << Stack.top() << " ";Stack.pop();}
}int main() {Graph g(6);g.addEdge(5, 2);g.addEdge(5, 0);g.addEdge(4, 0);g.addEdge(4, 1);g.addEdge(2, 3);g.addEdge(3, 1);cout << "拓扑排序结果: ";g.topologicalSort();return 0;
}

        我们来分析以下上面的算法,其实关于dfs,还是那句“一条路走到黑”来概括更为合适。我们在只要遍历到入度为0的点,就以这个点为起点开始dfs,在dfs过程中遇到的所有点都先加入栈,最后,再依次弹出栈。光说不好理解,我们看图来理解一下:

        最后再依次出栈,就是拓扑排序。 

Kahn算法实现(基于入度)

        其实一般提到拓扑排序,都是基于Kahn算法实现的。Kahn算法通过维护入度表实现拓扑排序,步骤如下:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;vector<int> topologicalSort(int V, vector<vector<int>>& adj) {vector<int> inDegree(V, 0);queue<int> q;vector<int> result;// 计算每个顶点的入度for (int u = 0; u < V; u++)for (int v : adj[u])inDegree[v]++;// 入度为0的顶点入队for (int i = 0; i < V; i++)if (inDegree[i] == 0) q.push(i);while (!q.empty()) {int u = q.front();q.pop();result.push_back(u);for (int v : adj[u])if (--inDegree[v] == 0)q.push(v);}if (result.size() != V) {cout << "图中存在环!" << endl;return {};}return result;
}int main() {int V = 6;vector<vector<int>> adj(V);adj[5] = {2, 0};adj[4] = {0, 1};adj[2] = {3};adj[3] = {1};vector<int> sorted = topologicalSort(V, adj);for (int v : sorted) cout << v << " ";return 0;
}

        我们还是根据图来理解一下:

 

关键注意事项

  • 两种方法时间复杂度均为 O(V+E),其中V为顶点数,E为边数
  • DFS实现的结果是逆序的,需要通过栈反转输出
  • Kahn算法可以直接检测图中是否存在环(当结果集大小不等于顶点数时)

3、应用场景扩展

拓扑排序可应用于:

  • 编译器中的指令调度
  • 软件包依赖管理(如apt-get/yum)
  • 课程选修顺序规划
  • 任务调度系统

        两种实现方式各有优劣:DFS代码简洁但需要额外空间存储栈;Kahn算法更直观且能直接检测环,但需要维护入度表。根据具体场景选择合适实现。但是我个人认为还是Kahn算法更好想一些。

        好了,今天的内容就分享到这,我们下期再见! 

 

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

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

相关文章

Ubuntu18.04 系统重装记录

Ubuntu18.04 系统重装记录 1 安装google拼音 https://blog.csdn.net/weixin_44647619/article/details/144720947 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdo…

Maven常用知识总结

Maven常用知识总结Maven 安装与配置windows mvn安装与配置IntelliJ IDEA 配置IntelliJ IDEA 配置系统mavenIntellij IDEA Maven使用IntelliJ IDEA 不能运行项目常见问题pom.xml 常用标签讲解parentgroupId artifactId versiondependencypropertiespluginpackagingdependencyMan…

PHP框架在大规模分布式系统的适用性如何?

曾几何时&#xff0c;PHP被贴上“只适合小网站”的标签。但在技术飞速发展的今天&#xff0c;PHP框架&#xff08;如Laravel、Symfony、Hyperf、Swoft等&#xff09; 早已脱胎换骨&#xff0c;勇敢地闯入了大规模分布式系统的疆域。今天&#xff0c;我们就来聊聊它的真实战斗力…

DC-DC降压转换5.5V/3A高效率低静态同步降压转换具有自适应关断功能

概述&#xff1a;PC1032是一款高效且体积小巧的同步降压转换器&#xff0c;适用于低输入电压应用。它是紧凑设计的理想解决方案。其2.5V至5.5V的输入电压范围适用于几乎所有电池供电的应用。在中等至重负载范围内&#xff0c;它以1.5MHz&#xff08;典型值&#xff09;的PWM模式…

min_25筛学习笔记+牛客多校02E

本来没有学习这种较难的算法的想法的&#xff0c;因为比赛也做不到这种难度的题&#xff0c; 但是最近打牛客多校02&#xff0c;有一题要求 [1,n][1,n][1,n] 中素数的个数&#xff0c;我以为是像莫反一样容斥&#xff0c;但是后面感觉不行。赛后知道是用 min_25 筛来求&#xf…

FunASR Paraformer-zh:高效中文端到端语音识别方案全解

项目简介 FunASR 是阿里巴巴达摩院开源的端到端语音识别工具箱,集成了多种语音识别、语音活动检测(VAD)、说话人识别等模块。其中 paraformer-zh 和 paraformer-zh-streaming 是针对中文语音识别任务优化的端到端模型,分别适用于离线和流式场景。Paraformer 采用并行 Tran…

数据结构自学Day9: 二叉树的遍历

一、二叉树的遍历“遍历”就是按某种规则 依次访问树中的每个节点&#xff0c;确保 每个节点都被访问一次且只访问一次遍历&#xff1a;前序 中序 后序&#xff08;深度优先&#xff09;&#xff0c;层序&#xff08;广度优先&#xff09;类型遍历方法特点深度优先遍历前序、中…

Leetcode(7.16)

求二叉树最小深度class Solution {public int minDepth(TreeNode root) {if (root null) {return 0;}Queue<TreeNode> queue new LinkedList<>();queue.offer(root);int depth 0;while (!queue.isEmpty()) {depth;int levelSize queue.size();for (int i 0; i…

Go从入门到精通(25) - 一个简单web项目-实现链路跟踪

Go从入门到精通(25) 一个简单web项目-实现链路跟踪 文章目录Go从入门到精通(25)前言为什么需要分布式链路跟踪&#xff1f;go实现链路跟踪搭建zipkin 服务安装依赖添加tracing包&#xff0c;OpenTelemetry 和Zipkin在 Gin 中集成 OpenTelemetry 中间件log包添加获取traceId方法…

2025年最新秋招java后端面试八股文+场景题

一、Java核心八股文&#xff08;2025年最新版&#xff09;1. Java基础HashMap vs ConcurrentHashMapHashMap&#xff1a;非线程安全&#xff0c;JDK1.8后采用数组链表/红黑树&#xff0c;扩容时可能死循环&#xff08;JDK1.7&#xff09;。ConcurrentHashMap&#xff1a;JDK1.8…

esp32 sd卡

ref&#xff1a; platform io & arduino Boards — PlatformIO latest documentation https://github.com/espressif/arduino-esp32/blob/master/libraries/SD_MMC/README.md SD 卡实验 | 极客侠GeeksMan GitHub - fabianoriccardi/ESPLogger: An Arduino library pro…

Java学习--------消息队列的重复消费、消失与顺序性的深度解析​

在 Java 分布式系统开发中&#xff0c;消息队列的应用已十分普遍。但随着业务规模扩大&#xff0c;消息的重复消费、意外消失、顺序错乱等问题逐渐成为系统稳定性的隐患。本文将从 Java 开发者的视角&#xff0c;深入分析这三大问题的产生原因、业务后果&#xff0c;并结合具体…

【Oracle】centos7离线静默安装oracle11g(p13390677_112040)

博文地址&#xff1a;https://blog.csdn.net/gitblog_06670/article/details/142569814 仓库地址&#xff1a;https://gitcode.com/Open-source-documentation-tutorial/31eb1/?utm_sourcedocument_gitcode&indexbottom&typecard 参考安装地址&#xff1a; 收费版&…

智能设备畅想

### 智能设备畅想 突然想到了一个好主意 因为最近在查无人机的相关资料&#xff08;很早之前就想搞个无人机玩玩但始终没有买&#xff09; 在了解自组装方面的内容时&#xff0c;和AI沟通了下 正好之前组装的 小智AI 基本上已经完善了&#xff0c;也正在考虑其在其他方向拓展的…

SpringAI——ChatModel

我的前面一篇文章&#xff08;SpringAI——ChatClient配置与使用&#xff09;中讲了ChatClient&#xff0c;它是一个构建于 ChatModel 之上的高层封装&#xff0c;它提供了更丰富的对话交互能力。可以这么说ChatModel相当于发动机&#xff0c;ChatClient相当于一台含有发动机、…

Zabbix监控K8S的PV信息详细教程!

文将介绍如何使用Zabbix自定义键值脚本方式监控K8S的PV卷状态等信息。 在Kubernetes (K8S) 中&#xff0c;PersistentVolume (PV) 是集群中的一个抽象层&#xff0c;它代表了底层存储资源&#xff0c;例如网络存储系统&#xff08;如NFS、Ceph、GlusterFS等&#xff09;或本地存…

wx小程序原生开发使用高德地图api

第一步&#xff1a;注册高德地图api的key第二步&#xff1a;下载amap-wx.js 放到项目的某个目录第三步&#xff1a;配置app.json&#xff08;必须&#xff0c;因为需要定位功能&#xff0c;&#xff09;"requiredPrivateInfos": ["getLocation"],"per…

如何通过mac的前24bit,模糊确认是那一台什么样的设备

MAC Address Lookup - MAC/OUI/IAB/IEEE Vendor Manufacturer Search Wireshark • Go Deep 上面这两个网址提供了&#xff0c;正对mac 的前24位&#xff0c;查找对应的网络设备厂商信息&#xff0c;可以让我们在运维过程中模糊的判断大约是什么型号的设备 使用macvendorloo…

【爬虫】04 - 高级数据存储

爬虫04 - 高级数据存储 文章目录爬虫04 - 高级数据存储一&#xff1a;加密数据的存储二&#xff1a;JSON Schema校验三&#xff1a;云原生NoSQL(了解)四&#xff1a;Redis Edge近端计算(了解)五&#xff1a;二进制存储1&#xff1a;Pickle2&#xff1a;Parquet一&#xff1a;加…

UDP和TCP的主要区别是什么?

在网络通信中&#xff0c;TCP&#xff08;传输控制协议&#xff09;和UDP&#xff08;用户数据报协议&#xff09;是两种核心的传输层协议。它们各自的特点和应用场景截然不同&#xff0c;理解两者的区别对于选择合适的通信方式至关重要。本文将通过几个关键点&#xff0c;用简…