拓扑排序(算法基础)

server/2025/3/30 2:05:17/

拓扑排序

试想这样一个问题,我们做事情事情本身之间有先决条件。例如我们希望能在这些事情之间找到不破坏依赖关系的前提下对整个结果排序。这被称为拓扑排序。假设下面的例子,每个节点称为一个课程。

C1
C3
C8
C4
C2
C5
C6
C9
C7

我们学习C3则需要先学C2和C1。也就是说我们应该先学没有任何依赖的课程(没有被指向的节点)。于是我们定义节点的入度表示有多少指向其的节点。例如C3的入度为2,因为有两个节点C1和C2指向它
C3课程需要C1和C2先修课程,如果C1当前已经完成,此时想上C3则需要完成剩下的课程C2。当然我们也可以先完成C2再完成C1,也就是说拓扑排序的结果不一定是唯一的。我们上了某门课则需要将次课程指向的课程入度-1(或者也可以说将指向的边删除C1–>C3的边删除)。于是我们可以简单地总结拓扑排序

  1. 找到图中入度为0的点加入结果
  2. 将节点关联的其它节点的入度-1
  3. 在剩下节点中找到入度为0的节点重复操作1,2直到最后所有的节点入度都为0为止。

算法分析

根据上面的算法我们需要知道:

  1. 首先知道每个节点的入度值。
  2. 根据节点找到其指向的节点。
  3. 快速找到入度为0的节点。

算法流程分析

入度表:

C1C2C3C4C5C6C7C8C9
002212211

为了能顺序访问,我们将入度为0的节点放入队列。算法执行:

  1. 将入度为0的C1、C2加入队列
  2. 将队列头的C1取出,根据C1找到其相连的C3和C8节点,然后将C3和C8的入度-1。此时图如下:
C3
C4
C2
C5
C6
C8
C9
C7
C1C2C3C4C5C6C7C8C9
001212201

这时我们发现C8入度为0,加入队列。此时队列中元素为[C2,C8]
3. C2出队列,将C3和C5的入度-1。

C3
C4
C5
C6
C8
C9
C7
C1C2C3C4C5C6C7C8C9
000202201

此时C3和C5已经入度为0,入队列,此时队列中元素[C8,C3,C5]
4. C8出队列,然后C9入度-1

C3
C4
C5
C6
C9
C7
C1C2C3C4C5C6C7C8C9
000202200

此时C9入度为0,将C9加入队列。此时队列中[C3,C5,C9]
5. C3出队列,C4入度-1

C5
C4
C6
C9
C7
C1C2C3C4C5C6C7C8C9
000102200

此时没有节点入度为0,则不需加入任何节点到队列中。队列中元素[C5,C9]
6. C5出队列,C4和C6入度-1

C4
C6
C9
C7
C1C2C3C4C5C6C7C8C9
000001200

此时C4入度为0,将C4加入队列。队列中元素[C9,C4]
7. C9出队列,C7入度-1。

C4
C6
C7
C1C2C3C4C5C6C7C8C9
000001100

此时队列中元素为C4
8. C4出队列,C6、C7入度-1。

C7
C6
C1C2C3C4C5C6C7C8C9
000000000

此时C6和C7入度都为0,将其加入队列,此时队列元素[C6,C7]
9.最后C6、C7出队列。队列为空,则可以拓扑排序。

什么样的情况无法拓扑排序?

C4
C6
C7

此时没有节点入度为0。核心在于C4课程依赖于C7,而C7同样依赖于C4。

实践

def canFinish(numCourses: int, prerequisites: List[List[int]]) -> bool:node_link_nodes = {}node_indgree = {}indegree = set()for node in prerequisites:if node[1] not in node_indgree:node_indgree[node[1]] = 1else:node_indgree[node[1]] += 1# 入度不为0的节点加入indegreeindegree.add(node[1])if node[0] not in node_link_nodes:node_link_nodes[node[0]] = [node[1]]else:node_link_nodes[node[0]].append(node[1])nodes = deque(set(range(numCourses)) - indegree)out = []while len(nodes) > 0:no_indegree = nodes.popleft()out.append(no_indegree)if no_indegree in node_link_nodes:for node in node_link_nodes[no_indegree]:node_indgree[node] -= 1if node_indgree[node] == 0:nodes.append(node)return len(out) == numCoursesdef findOrder(numCourses: int, prerequisites: List[List[int]]) -> List[int]:indegree = [0] * numCourses# 节点和其连接的相邻节点adj = {}res = []for edge in prerequisites:indegree[edge[0]] += 1for x, y in prerequisites:if y not in adj:adj[y] = []adj[y].append(x)visited = [False for i in range(numCourses)]node_indeies = []for node_index, value in enumerate(indegree):if value == 0 and not visited[node_index]:node_indeies.append(node_index)visited[node_index] = Truewhile len(node_indeies) > 0:joined_node = node_indeies.pop(0)res.append(joined_node)if joined_node in adj:for related_index in adj[joined_node]:indegree[related_index] -= 1if indegree[related_index] == 0 and not visited[related_index]:node_indeies.append(related_index)visited[related_index] = Trueif joined_node in adj:adj.pop(joined_node)return res if len(res) == numCourses else []

http://www.ppmy.cn/server/179390.html

相关文章

【MySQL | 七、存储引擎是什么?】

存储引擎是什么?作用于哪里? 1. 存储引擎的定义 存储引擎(Storage Engine)是数据库管理系统中负责 数据的存储、检索和管理 的核心组件。它决定了数据如何存储在磁盘上,以及如何从磁盘中读取数据。存储引擎是数据库与…

视频网站服务器网络连接不稳定该如何解决?

当视频网站行业的服务器出现网络连接稳定,导致用户在流量网站或视频的过程中出现卡顿或是网络中断的情况,企业都有哪些办法进行解决呢? 企业可以选择优化服务器中的网络配置,通过将网站内容缓存到多个服务器节点,缩短用…

常见框架漏洞(一)----Thinkphp(TP)

Thinkphp框架介绍: ThinkPHP是为了简化企业级应⽤开发和敏捷WEB应⽤开发⽽诞⽣的,是⼀个快速、兼容⽽ 且简单的轻量级国产PHP开发框架,诞⽣于2006年初,原名FCS,2007年元旦正式更名为 ThinkPHP,遵循Apache…

Java-SpringBootWeb入门、Spring官方脚手架连接不上解决方法

一. Spring 官网:Spring | Home Spring发展到今天已经形成了一种开发生态圈,Spring提供了若干个子项目,每个项目用于完成特定的功能(Spring全家桶) Spring Boot可以帮助我们非常快速的构建应用程序、简化开发、提高效率 。 二. Spring Boot入…

在Ubuntu 22.04 中安装Docker的详细指南

这里写目录标题 前言一、安装 Docker1. 卸载旧版本(如有)2. 更新系统并安装依赖工具3. 添加 Docker 官方 GPG 密钥4. 设置 Docker 仓库5. 安装 Docker Engine6. 验证安装 二、配置 Docker 镜像加速1. 修改 Docker 配置文件2. 重启 Docker 服务3. 验证加速…

centos7安装单机kafka

下载安装包并进行解压:kafka_2.12-2.2.1.tgz #解压 tar -zxvf /usr/local/kafka/kafka_2.12-2.2.1.tgzconsumer.properties 是消费者的相关配置producer.properties 是生产者的相关配置server.properties 是 kafka 服务的配置zookeeper.properties 是 zookeeper 的…

初级:数组与字符串面试题深度剖析

一、引言 在Java开发中,数组和字符串是最常用的数据结构之一。面试官通过相关问题考察候选人对数组和字符串的理解和运用能力,以及在实际开发中解决相关问题的经验。本文将深入剖析常见的数组与字符串面试题,结合实际开发场景,帮…

LeetCode --- 441周赛

题目列表 3487. 删除后的最大子数组元素和 3488. 距离最小相等元素查询 3489. 零数组变换 IV 3490. 统计美丽整数的数目 一、删除后的最大子数组元素和 题目要求我们去掉一些元素使得数组之和最大,即只取数组中 ≥ 0 \ge0 ≥0 的数字,同时数组中不能包…