图搜索算法 - 拓扑排序

news2024/11/25 20:36:52

相关文章:
数据结构–图的概念
图搜索算法 - 深度优先搜索法(DFS)
图搜索算法 - 广度优先搜索法(BFS)

拓扑排序

概念

几乎所有的工程都可分为若干个称作活动的子工程,而这些子工程之间,通常受着一定条件的约束,如其中某些子工程的开始必须在另一些子工程完成之后。对整个工程和系统,人们关心的是两个方面的问题:一是工程能否顺利进行;二是估算整个工程完成所必须的最短时间。这样两个问题都是可以通过对有向图进行拓扑排序和关键路径操作来解决的。当然这里说的工程,泛指一切的项目工程,如指令调度,数据序列化,软件安装包依赖关系,代码编译任务顺序等。

拓扑排序是对项目工程的排序,那么先来构建一个项目,比如这个项目制作番茄炒蛋,如图所示。
在这里插入图片描述
对一个有向无环图G进行拓扑排序,是将G中所有结点排成一个线性序列,使得图中任意一对结点u和v,u在线性序列中总是出现在v之前。如上面做菜顺序0-1-2-3-4-5-9-6-7-8,也可以0-3-2-1-4-5-9-6-7-8这样,都满足拓扑次序(Topological Order),也就是番茄总是要洗了再切,番茄要切成小块再炒,不能整个炒,这样的顺序不会改变,这简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

注意:偏序是指集合中仅有部分元素可比较大小(或先后),全序是指集合中所有元素可比较大小(或先后)。

原理

拓扑排序算法是基于深度优先搜索(以下简称DFS)的基础上做调整的,首先查看例子用DFS计算是怎样的结果,从结点【1】开始继续搜索,结果是0-1-4-7-8-2-5-9-3-6。显然这是不是拓扑排序的结果,因此要略为修改DFS。从一个结点出发,DFS是马上输出再递归进入相邻结点,这是不适合拓扑排序。这里应该先访问相邻的结点,若还有相邻结点,继续深入下一个结点,当所有相邻的结点都进入栈后,才把该结点推入栈,以下手动模拟此运算过程。

(1)首先初始化列表【visited】全部为【False】,所有结点刚开始都是未访问以及临时栈【stack】为空。然后从结点【0】开始,然后发现有3个结点,然后继续访问结点【1】,同理一直深入访问结点【4】、结点【7】和结点【8】,到这里没有发现相邻结点,那边我们把结点【8】入栈,然后回退到结点【7】,同样它也没有其他相邻结点,同样也入栈,同理结点【4】和结点【1】也一起入栈,如表所示。
在这里插入图片描述
(2)回到结点【0】,发现还有相邻结点【2】和【3】,然后我们访问结点【2】,同样一层层深入结点,直到结点【8】,由于它已经访问过了,所以不需要再次放到栈里面。然后回退到上一个结点【9】就可以放到栈里面,同理结点【5】和【2】也一起入栈,如表所示。
在这里插入图片描述
(3)继续访问未访问结点【3】,然后进入结点【6】,然后再想进一步访问结点【7】,发现它也在栈中,所以可以停止递归,把结点【6】推入栈,再把结点【3】入栈,这时候结点【0】所有相邻结点也访问完,也可以把它入栈,如表所示。
在这里插入图片描述
(4)这时候从栈中输出结果,从顶部结点开始结构为0-3-6-2-5-9-1-4-7-8,符合了拓扑排序的要求。
在编写代码前,先来分析算法的复杂度,如果图中有N个结点,E条边,在拓扑排序的过程中,因为复用【Graph】类,则使用邻接列表来表示图,所以查找所有结点的邻接结点所需时间为O(N),访问结点的邻接点所花时间为O(E),总的时间复杂度为O(N+E)。空间复杂度为递归深度,极限情况就是结点总数,则为O(N)。

class Graph(): 
    """图类"""
    def __init__(self): 
        self.graph = {}  # 初始化图的邻接列表
    def add_edge(self,u,v): 
        if v:
            point = self.graph.get(u) # 尝试获取结点u
            if point:
                point.append(v)       # 若存在直接添加u-v的边
            else:
                self.graph[u] = [v]   # 若不存在,则先初始化u结点,然后再添加u-v的边
        else:
            self.graph[u] = list()  # 如果v没有值,添加一个空列表

class GraphTopological(Graph):
    """解决拓扑排序问题"""
    def topological_sort_util(self, v, visited, stack): 
        visited[v] = True       # 该结点变为已访问
        for i in self.graph[v]: 
            if visited[i] == False: # 结点未访问递归调用函数
                self.topological_sort_util(i, visited, stack) 
        # 相邻结点都访问结束后,把该结点放到栈中
        stack.insert(0,v)  # 把新入栈元素放在表头
    def topological_sort(self):
        # 拓扑排序主程序
        visited = {}   # 初始化参数是否已经访问
        stack = [] # 初始化参数,用列表表示临时栈为空
        for key in self.graph.keys():
            visited[key] = False     # 值为未访问状态
        for node in self.graph.keys(): # 遍历所有结点 
            if visited[node] == False: # 结点是否已经访问
                self.topological_sort_util(node, visited, stack) # 递归进入结点
        print(stack) #把栈保存结果输出

创建【TopologicalGraph】类继承上面【Graph】类,复用构成邻接列表的过程。然后用列表构成栈,把递归结果保存在列表中,最后从栈表头开始输出结果便是拓扑排序的结果,现在用例子来测试结果是否符合预期。

g = GraphTopological() 
g.add_edge(0, 1) # 录入图的边
g.add_edge(0, 2) 
g.add_edge(0, 3) 
g.add_edge(1, 4) 
g.add_edge(2, 5) 
g.add_edge(3, 6)
g.add_edge(4, 7)
g.add_edge(5, 9)
g.add_edge(6, 7)
g.add_edge(7, 8)
g.add_edge(8, None)
g.add_edge(9, 8)
g.topological_sort() # 输出:[0, 3, 6, 2, 5, 9, 1, 4, 7, 8]

结果和刚才手动计算是一样,如果调换输入顺序,把第二行放到第四行,拓扑排序的结果如下。

[0, 1, 4, 3, 6, 7, 2, 5, 9, 8]

结果只是改变了遍历结点【1】,【2】和【3】的顺序,结果还是满足拓扑次序。

更多内容

想获取完整代码或更多相关图的算法内容,请查看我的书籍:《数据结构和算法基础Python语言实现》

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

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

相关文章

Figma 高效技巧:设计系统中的图标嵌套

Figma 高效技巧:设计系统中的图标嵌套 在设计中,图标起着不可或缺的作用。一套便捷易用的图标嵌套方法可以有效提高设计效率。 分享一下我在图标嵌套上走过的弯路和经验教训。我的图标嵌套可以分三个阶段: 第一阶段:建立图标库 一…

洛谷官方提单——【入门4】数组——python

洛谷官方提单——【入门4】数组 小鱼比可爱题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示代码 小鱼的数字游戏题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示数据规模与约定 代码 【深基5.例3】冰雹猜想题目描述输入格式输出格式样例 #1样例输入 …

24深圳杯ABCD成品论文47页+各小问代码+图表

A题多个火箭残骸的准确定位: A题已经更新完22页完整版论文+高清无水印照片+Python(MATLAB)代码简单麦麦https://www.jdmm.cc/file/2710544/ 问题1:单个残骸的音爆位置确定 建模思路: 1. 声波传…

Codeforces Round 942 (Div.1) (Div. 2) 2A~2D

2A.Contest Proposal(枚举) 题意: 一个竞赛包含 n n n个问题,第 i i i个问题的难度预计最多为 b i b_i bi​。现在已经有 n n n个问题提案,第 i i i个问题的难度为 a i a_i ai​。最初, a 1 , a 2 , … ,…

为什么现在越来越多的人会选择陪诊

现在越来越多的人选择陪诊的原因有多方面。 首先,随着人口老龄化、医疗资源分配不均等问题的日益突出,许多老年人和病患在就医过程中面临诸多困难,如挂号、排队、取药等繁琐的手续和流程。陪诊服务能够为他们提供极大的便利,帮助…

spring模块(六)spring监听器(2)@EventListener

一、介绍 监听器的简化写法 二、原理 三、使用 Slf4j Component public class MyTask {EventListenerpublic void onApplicationEvent(ApplicationEvent event) {if (event instanceof ContextRefreshedEvent) {log.info("监听到 ContextRefreshedEvent...");}if…

[图解]DDD架构好简单我学会了-学会也没啥用

1 00:00:03,720 --> 00:00:05,920 内部共有,首先是内部的 2 00:00:08,150 --> 00:00:09,220 所以不能说什么 3 00:00:09,630 --> 00:00:10,730 不能跟外部连在一起 4 00:00:10,740 --> 00:00:15,280 比如说,功能架构,可以吗 …

使用Linux命令时,前面加sudo和不加有什么区别?

在使用cmake命令编译时,前面加上sudo和不加主要有以下区别: 权限: 使用sudo:当您在命令前加上sudo时,表示您以超级用户的权限执行该命令。这通常用于需要访问受限制的系统文件或执行需要更高权限的操作。不使用sudo&am…

Swift 字符串和字符

字符串和字符 一、字符串字面量1、多行字符串字面量2、字符串字面量的特殊字符3、扩展字符串分隔符 二、初始化空字符串三、字符串可变性四、字符串是值类型五、使用字符六、连接字符串和字符七、字符串插值八、Unicode1、Unicode 标量2、可扩展的字形群集 九、计算字符数量十、…

『ZJUBCA Collaboration』WTF Academy 赞助支持

非常荣幸宣布,浙江大学区块链协会收到WTF Academy的赞助与支持,未来将共同开展更多深度合作。 WTF Academy是开发者的Web3开源大学,旨在通过开源教育让100,000名开发者进入到Web3。截止目前,WTF开源教程在GitHub收获超15,000 ⭐&a…

五一假期Llama 3之魔改不完全攻略(Part 2)

2024年4月18日,Meta AI 正式宣布推出 Llama 3,这标志着开源大型语言模型(LLM)领域的又一重大进步。如同一颗重磅炸弹, Llama 3 以其卓越的性能和广泛的应用前景,预示着 AI 技术的新时代。 目前开源的是Lla…

H62410A dcdc 24V30V36V48V60V72V100V降压12V/5V1A 恒压电源芯片IC

DCDC 24V-30V-36V-48V-60V-72V-100V降压至12V/5V 1A恒压电源芯片IC的工作原理主要基于开关调节和PWM(脉冲宽度调制)控制。 首先,芯片内部通常包含一个高速开关,通常是一个MOSFET(金属氧化物半导体场效应晶体管&#x…

MySQL·内置函数

目录 函数 日期函数 案例1:创建一张表,记录生日 案例2:创建一个留言表 案例3:请查询在2分钟内发布的帖子 字符串函数 案例1: 获取emp表的ename列的字符集 案例2:要求显示exam_result表中的信息&am…

XShell 无法连上 VirtualBox的系统问题排查

之前一直都是可以正常使用的,过了一段时间之后,我发现无法使用XShell连接我之前安装的Centos 系统了。 我在centos中ping windows的IP地址,是可以 ping 通的, 百度也可以 ping 通,但是在 windows 中 ping centos的IP地…

湘潭大学数据库作业题完整答案

作业一: 考虑如下所示的关系数据库。这些关系上适当的主码是什么? 职工(姓名,街道,城市) 工作(姓名,公司名,工资) 公司(公司名,城市&a…

【时序大模型总结】学习记录(1)

1.TimeGPT-1 思路:在来自不同领域的大量数据上训练模型,然后对未见过的数据产生零样本的推断。 作者对TimeGPT进行了超过1000亿个数据点的训练,这些数据点都来自开源的时间序列数据。该数据集涵盖了广泛的领域,从金融、经济和天气…

卧式负压排渣放水器为煤矿添彩

拥有自己的一片小天地,让每个角落都充满生活的色彩。快来找到心仪的产品,为煤矿添彩! 一、排渣放水器的概述: 负压自动排渣放水器的型号为FYPZ,FY指负压放水器中的负压,PZ指自动排渣,FYPZ的全称…

初学C++——C++基础、变量、字面量、常量、数据类型、类型转换、变量命名规则、开发环境配置

文章目录 简介C 语言的特性C 开发环境配置C 变量,字面量和常量C 变量变量命名规则 C 字面量C 常量 C 数据类型C 基本数据类型派生数据类型 C 类型转换隐式类型转换C 显式转换 简介 C 是一种静态类型的,自由形式的(通常)编译的&…

css浮动(float)

浮动(Float) 在CSS中是一个重要的布局技术,它允许元素向左或向右移动,其周围的元素会重新排列。当一个元素被设置为浮动时,它会脱离正常的文档流,这意味着它不再占据原本在文档流中的空间,而会尽…

会声会影下载免费中文版 会声会影2023破解 会声会影中文汉化补丁包 会声会影永久激活版序列号免费 会声会影安装使用教程

会声会影是加拿大Corel公司制作的一款功能强大的视频编辑软件,正版英文名:Corel VideoStudio,具有图像抓取和编修功能,可以抓取,转换MV、DV、V8、TV和实时记录抓取画面文件,并提供有超过100 多种的编制功能…