代码随想录训练营 Day58打卡 图论part08 拓扑排序 dijkstra朴素版 + 堆优化版

代码随想录训练营 Day58打卡 图论part08

一、拓扑排序

例题:卡码117. 软件构建

题目描述
某个大型软件项目的构建系统拥有 N 个文件,文件编号从 0 到 N - 1,在这些文件中,某些文件依赖于其他文件的内容,这意味着如果文件 A 依赖于文件 B,则必须在处理文件 A 之前处理文件 B (0 <= A, B <= N - 1)。请编写一个算法,用于确定文件处理的顺序。
输入描述
第一行输入两个正整数 N, M。表示 N 个文件之间拥有 M 条依赖关系。
后续 M 行,每行两个正整数 S 和 T,表示 T 文件依赖于 S 文件。
输出描述
输出共一行,如果能处理成功,则输出文件顺序,用空格隔开。
如果不能成功处理(相互依赖),则输出 -1。
输入示例
5 4
0 1
0 2
1 3
2 4
输出示例
0 1 2 3 4
提示信息
文件依赖关系如下:

所以,文件处理的顺序除了示例中的顺序,还存在
0 2 4 1 3
0 2 1 3 4
等等合法的顺序。

该代码的目标是通过拓扑排序来解决有向图中的依赖关系问题。具体来说,给定 n 个文件以及它们的依赖关系(即 m 条边),要求我们找出一种拓扑顺序,使得每个文件都能在它所依赖的文件之后被执行。如果无法找到合法的拓扑排序(即存在环),则输出 -1。

拓扑排序的定义:
拓扑排序是指对一个有向无环图(DAG)中的节点进行排序,使得对于每一条有向边 (u, v),节点 u 出现在节点 v 之前。换句话说,拓扑排序可以用于确定某些任务之间的依赖顺序。

算法思路

  1. 入度表:首先,为每个节点(文件)构建一个入度表,表示每个节点有多少条边指向它。即,如果一个文件有 k 个依赖的文件,那么它的入度为 k。

  2. 依赖关系图:使用邻接表(umap)来记录每个文件所依赖的其他文件。每个节点 s 指向其依赖的文件 t。

  3. 初始化队列:将所有入度为 0 的节点(即没有依赖的文件)加入队列中,这些节点可以作为排序的起始点。

  4. BFS 过程:

    (1)从队列中逐一取出节点,将其加入结果列表。
    (2)对每个节点 cur 的依赖节点(指向的文件)进行处理,更新它们的入度表(减少它们的入度)。
    (3)如果某个节点的入度变为 0,表示它的所有依赖文件已经处理完毕,可以加入队列进行处理。

  5. 检测环路:如果拓扑排序完成后,结果中包含的节点数不等于总节点数,说明存在环路,无法完成排序,此时返回 -1。

代码实现

from collections import deque, defaultdictdef topological_sort(n, edges):inDegree = [0] * n  # inDegree 数组记录每个节点的入度,初始为0umap = defaultdict(list)  # 邻接表,记录节点之间的依赖关系,默认为空列表# 构建图和入度表for s, t in edges:inDegree[t] += 1  # 每当一个节点被指向,其入度增加umap[s].append(t)  # s 指向 t,添加到邻接表中# 初始化队列,将所有入度为0的节点加入队列queue = deque([i for i in range(n) if inDegree[i] == 0])result = []  # 结果数组,用于存储拓扑排序的结果# 开始广度优先搜索(BFS)while queue:cur = queue.popleft()  # 当前队列中的节点,已无任何依赖result.append(cur)  # 将其加入结果列表中for file in umap[cur]:  # 遍历该节点指向的所有节点inDegree[file] -= 1  # 当前节点的出度边被移除,其指向节点的入度减1if inDegree[file] == 0:  # 如果入度变为0,说明该节点可以被处理queue.append(file)  # 将其加入队列# 如果拓扑排序中包含所有节点,说明排序成功;否则,说明有环if len(result) == n:print(" ".join(map(str, result)))  # 打印排序结果else:print(-1)  # 如果结果不包含所有节点,输出-1,表示图中有环# 主程序,读取输入
if __name__ == "__main__":# 读取节点数量 n 和边的数量 mn, m = map(int, input().split())# 读取 m 条边,每条边由两个整数 s 和 t 表示edges = [tuple(map(int, input().split())) for _ in range(m)]# 调用拓扑排序函数topological_sort(n, edges)

卡码题目链接
题目文章讲解

二、dijkstra(朴素版)

例题:卡码47. 参加科学大会

题目描述
小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。
小明的起点是第一个车站,终点是最后一个车站。然而,途中的各个车站之间的道路状况、交通拥堵程度以及可能的自然因素(如天气变化)等不同,这些因素都会影响每条路径的通行时间。
小明希望能选择一条花费时间最少的路线,以确保他能够尽快到达目的地。
输入描述
第一行包含两个正整数,第一个正整数 N 表示一共有 N 个公共汽车站,第二个正整数 M 表示有 M 条公路。
接下来为 M 行,每行包括三个整数,S、E 和 V,代表了从 S 车站可以单向直达 E 车站,并且需要花费 V 单位的时间。
输出描述
输出一个整数,代表小明从起点到终点所花费的最小时间。
输入示例
7 9
1 2 1
1 3 4
2 3 2
2 4 5
3 4 2
4 5 3
2 6 4
5 7 4
6 7 9
输出示例
12
提示信息
能够到达的情况:
如下图所示,起始车站为 1 号车站,终点车站为 7 号车站,绿色路线为最短的路线,路线总长度为 12,则输出 12。

不能到达的情况:
如下图所示,当从起始车站不能到达终点车站时,则输出 -1。

Dijkstra算法的基本步骤:

  1. 初始化:

    设定源点到自己的距离为0,其他节点的距离为无穷大。
    使用一个布尔数组 visited 来记录哪些节点已经被处理过,防止重复处理。

  2. 选择未访问的节点中距离源点最近的节点:
    遍历所有未访问的节点,选择距离源点最近的节点 cur。

  3. 更新与该节点相连的未访问节点的最短距离:
    对于当前节点 cur,检查与其相连的所有节点,更新它们到源点的距离。如果通过 cur 到达某个未访问节点的距离小于已知最短距离,则更新该节点的最短距离。

  4. 重复选择节点,直到所有节点都被访问过,或者没有可到达的未访问节点。

实现细节:

  • 邻接矩阵:我们使用一个 grid 矩阵来表示图,grid[i][j] 表示从节点 i 到节点 j 的边权重。初始化时,所有边的权重为无穷大float(‘inf’),表示没有连接。
  • 距离数组:minDist 用于记录源点到每个节点的最短距离。
  • 访问标记数组:visited 用于记录哪些节点已经处理完毕。

版本一 dijkstra(朴素版)

import sysdef dijkstra(n, m, edges, start, end):# 初始化邻接矩阵 grid,大小为 (n+1) x (n+1),初始值为无穷大,表示没有连接的边grid = [[float('inf')] * (n + 1) for _ in range(n + 1)]# 填充邻接矩阵,将每条边的权重记录到矩阵中for p1, p2, val in edges:grid[p1][p2] = val  # 从节点 p1 到节点 p2 的边权重为 val# 初始化最短距离数组 minDist,所有节点的最短距离初始为无穷大minDist = [float('inf')] * (n + 1)# 初始化访问数组 visited,记录每个节点是否已经被访问visited = [False] * (n + 1)# 起点的最短距离为0(起点到起点的距离为0)minDist[start] = 0# 进行 n 次遍历,每次选择一个节点加入到已处理的集合中for _ in range(1, n + 1):  # 遍历所有节点minVal = float('inf')  # 当前最小的距离cur = -1  # 当前选择的节点# 遍历所有节点,选择未访问过的且距离源点最近的节点for v in range(1, n + 1):if not visited[v] and minDist[v] < minVal:minVal = minDist[v]cur = vif cur == -1:  # 如果没有找到未访问的节点,提前结束break# 将当前选中的节点标记为已访问visited[cur] = True# 更新与当前节点相邻的未访问节点的距离for v in range(1, n + 1):# 如果 v 未访问且有从 cur 到 v 的边,并且通过 cur 到 v 的距离比现有的距离更短if not visited[v] and grid[cur][v] != float('inf') and minDist[cur] + grid[cur][v] < minDist[v]:# 更新节点 v 的最短距离minDist[v] = minDist[cur] + grid[cur][v]# 返回从起点 start 到终点 end 的最短距离# 如果终点的距离仍为无穷大,表示不可达,返回 -1return -1 if minDist[end] == float('inf') else minDist[end]# 主程序,处理输入并调用 Dijkstra 算法
if __name__ == "__main__":input = sys.stdin.read  # 读取输入data = input().split()  # 按空格分割输入数据n, m = int(data[0]), int(data[1])  # n 表示节点数,m 表示边数# 读取 m 条边,每条边由起点 p1、终点 p2 和边权重 val 组成edges = []index = 2for _ in range(m):p1 = int(data[index])p2 = int(data[index + 1])val = int(data[index + 2])edges.append((p1, p2, val))  # 将边加入到 edges 列表中index += 3# 起点为 1,终点为 nstart = 1  # 设置起点end = n    # 设置终点# 调用 Dijkstra 算法计算从起点到终点的最短路径result = dijkstra(n, m, edges, start, end)# 输出最短路径的距离print(result)

版本二 dijkstra(堆优化版)

堆优化的 Dijkstra 算法 相对于传统的 Dijkstra 算法,是通过使用 小顶堆 来加速寻找最短路径的节点。我们不再通过遍历所有节点来选择未访问且最近的节点,而是通过小顶堆高效地获取当前最小的路径节点。这种优化能显著减少计算量,尤其是在边稀疏的图中。

三部曲的改进:

  1. 选取最近的节点:我们通过一个小顶堆来存储每个节点到源点的最短路径,并每次从堆顶取出路径最短的节点进行处理。
  2. 标记为已访问:从堆中取出的节点为当前未访问的节点中距离最小的,标记该节点为已访问。
  3. 更新其他节点的最短距离:遍历该节点的所有相邻节点,更新它们到源点的最短距离,并将更新后的距离加入堆中。
import heapq  # 导入heapq库,用于实现小顶堆# 定义 Edge 类,表示一条边,包含目标节点和边的权值
class Edge:def __init__(self, to, val):self.to = to  # 边的目标节点self.val = val  # 边的权值# 堆优化的 Dijkstra 算法实现
def dijkstra(n, m, edges, start, end):# 初始化邻接表,使用列表存储与每个节点相连的边grid = [[] for _ in range(n + 1)]  # n + 1,因为节点编号从 1 开始# 构建邻接表,将每条边添加到对应的节点列表中for p1, p2, val in edges:grid[p1].append(Edge(p2, val))  # 记录 p1 到 p2 的边,权值为 val# 初始化最短距离数组,所有节点的初始最短距离为无穷大minDist = [float('inf')] * (n + 1)# 初始化访问数组,记录节点是否已被访问visited = [False] * (n + 1)# 小顶堆,堆中存储 (距离, 节点) 元组pq = []# 将起点加入堆中,起点到自身的距离为 0heapq.heappush(pq, (0, start))minDist[start] = 0  # 起点到自身的距离为 0# 处理堆中的节点while pq:cur_dist, cur_node = heapq.heappop(pq)  # 从堆中取出距离最近的节点if visited[cur_node]:  # 如果该节点已经访问过,跳过continuevisited[cur_node] = True  # 标记该节点为已访问# 遍历当前节点的所有邻接边for edge in grid[cur_node]:# 如果目标节点未访问过,且通过当前节点的路径更短,则更新该路径if not visited[edge.to] and cur_dist + edge.val < minDist[edge.to]:# 更新目标节点的最短距离minDist[edge.to] = cur_dist + edge.val# 将更新后的 (最短距离, 目标节点) 加入堆中heapq.heappush(pq, (minDist[edge.to], edge.to))# 如果终点的最短距离仍为无穷大,说明不可达,返回 -1;否则返回最短距离return -1 if minDist[end] == float('inf') else minDist[end]# 读取输入数据并调用 Dijkstra 算法
if __name__ == "__main__":# 输入节点数 n,边数 mn, m = map(int, input().split())# 输入每条边的信息,边的格式为 (起点, 终点, 权值)edges = [tuple(map(int, input().split())) for _ in range(m)]# 设置起点和终点start = 1  # 默认起点为 1end = n    # 默认终点为 n# 调用 Dijkstra 算法计算从起点到终点的最短路径result = dijkstra(n, m, edges, start, end)# 输出最短路径结果print(result)

详细说明:

  1. 邻接表的构建:
    grid = [[] for _ in range(n + 1)]:初始化一个邻接表,grid[i] 表示节点 i 的所有相邻节点及其边的权重。
    遍历输入的边,构建邻接表,将每条边加入到对应的起点节点的列表中。

  2. 小顶堆的使用:
    使用 Python 的 heapq 模块实现小顶堆。堆中存储 (距离, 节点) 元组,每次取出距离最小的节点。
    heapq.heappush(pq, (0, start)):将起点的距离(0)和起点节点加入堆中。
    heapq.heappop(pq):从堆中取出距离最小的节点,并对其进行处理。

  3. 更新最短路径:
    对于当前取出的节点 cur_node,遍历它所有的邻接节点,计算通过 cur_node 到达邻接节点的距离是否更短。如果更短则更新最短距离,并将其加入堆中。

  4. 提前退出:
    如果当前从堆中取出的节点已经访问过,直接跳过,这样可以避免重复处理。

  5. 返回结果:
    如果终点节点 end 的最短距离为无穷大,说明不可达,返回 -1;否则返回从起点到终点的最短距离。

堆优化的思路:

  • 使用小顶堆时,所有边的处理顺序已经由堆的结构自动排序成了从最短到最长的顺序。因此不需要每次遍历所有未访问节点来查找最短路径,这样可以显著提高算法效率。
  • 通过小顶堆优化后,Dijkstra 算法的时间复杂度从 O(n^2) 降低到 O(E log V),其中 E 是边的数量,V是节点的数量。这在处理稀疏图时效率非常高。

卡码题目链接
题目文章讲解

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

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

相关文章

勇于尝试,永远行动 - 《洛克菲勒写给儿子的38封信》读书笔记

两倍速听过好几遍的书&#xff0c;洛克菲勒的思想和志向&#xff0c;配得上他的成就。 “在尝试中寻找突破&#xff0c;在行动中成就自我。”这是洛克菲勒写给儿子的箴言&#xff0c;也是他一生的真实写照。在这38封信中&#xff0c;他不仅分享了自己的工作心得&#xff0c;更…

完整版订单超时自动取消功能

前几天对实习还是继续学习技术产生了抉择&#xff0c;问了一个前辈&#xff0c;他抛给我一个问题&#xff0c;怎么做15分钟订单自动取消&#xff0c;我说然后到时间之后&#xff0c;自动执行这个订单关闭业务&#xff0c;比如把锁了的库存给解开等等操作&#xff0c;然后在数据…

VTD激光雷达(3)——04_OptiX_SimulationDataFlow

文章目录 前言一、总结 前言 一、 1 总结

synchronized的详解、锁的升级过程和优缺点比较

本文 详细介绍Java中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级 锁、重量级锁&#xff0c;以及锁升级过程。 Java中每一个对象都可以作为锁。具体表现形式为以下三种形式&#xff1a; 对于普通的同步方法&#xff0c;锁是当前的实例对象对于静态同步方法&a…

cJSON-轻量级解析模块、字符串的神——编织STM32C8T6与阿里云信息传递的纽带

编写方向&#xff1a;本人就不泛泛的编写一篇什么一文学会cJSON了&#xff0c;没什么突出点&#xff0c;也就我水水字数&#xff0c;你们看来看去也不懂&#xff0c;本人是从上阿里云传信息接触的cJSON的&#xff0c;我就此写一篇针对性的文章&#xff0c;希望对大家有用&#…

研1日记12

1. 改19->10 2. 学习数据不平衡问题 1. 欠采样 合并两个样本数据 两种方式 1. 按原分布比例划分。sklearn中train_test_split里&#xff0c;参数stratify含义解析_traintestsplit参数stratify-CSDN博客 3.刘二大人 卷积操作 待看论文&#xff1a; 刘老师指导&#xff1a…

用于稀疏自适应深度细化的掩码空间传播网络 CVPR2024

目录 Masked Spatial Propagation Network for Sparsity-Adaptive Depth Refinement &#xff08;CVPR 2024&#xff09;用于稀疏自适应深度细化的掩码空间传播网络1 介绍2 算法流程2.1 问题建模2.2 Guidance Network2.3 MSPN 模块 3 实验结果3.1 稀疏度自适应深度细化对比试验…

图论篇--代码随想录算法训练营第六十一天打卡| Floyd 算法,A*算法

Floyd 算法&#xff08;求多源汇最短路&#xff09; 题目链接&#xff1a;97. 小明逛公园 题目描述&#xff1a; 小明喜欢去公园散步&#xff0c;公园内布置了许多的景点&#xff0c;相互之间通过小路连接&#xff0c;小明希望在观看景点的同时&#xff0c;能够节省体力&…

计算机二级office操作技巧——Excel篇

文章目录 函数公式总结写在前面五大基本函数sum求和函数average求平均函数max求最大值函数min求最小值函数count求个数函数 rank排名函数if逻辑判断函数条件求个数函数countif单条件求个数函数countifs多条件求个数函数 条件求和函数sumifs多条件求和函数sumproduct乘积求和函数…

算法刷题[比较两个字符串的最大公字符串(滑动窗口实现)]

题目&#xff1a;编程实现&#xff1a;找出两个字符串中最大公共子字符串,如"abccade","dgcadde"的最大子串为"cad" 代码如下所示&#xff1a; #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #inclu…

C语言实现贪吃蛇小游戏

✅博客主页:爆打维c-CSDN博客​​​​​​ &#x1f43e; &#x1f539;分享c语言知识及代码 &#x1f43e; 目录 游戏展示视频 一、项目准备工作 二、功能实现分析 1.游戏开始 a.设置本地化、创建窗口、标题 b.隐藏光标,封装定位光标的函数 c.打印欢迎界面及提示信息 …

网盘隐私照片泄露?教你如何保护自己的隐私照片!

网盘内的隐私照片 好兄弟最近遇到了一个困难&#xff1a;“我之前一直都是把照片存在网盘里面的&#xff0c;但是最近听说了某网盘的照片泄露了&#xff0c;自己的生活照啊&#xff0c;私密照啊都被人看光了&#xff0c;这太可怕了&#xff01;我现在也很担心自己的网盘上照片…

2021高教社杯全国大学生数学建模竞赛C题 Python代码演示

目录 问题一1.1 根据附件 1&#xff0c;对 402 家供应商的供货特征进行量化分析计算供货特征数据标准化对正向指标归一化对负向指标归一化 1.2 建立反映保障企业生产重要性的数学模型熵权法熵权法-TOPSISAHP 1.3 在此基础上确定 50 家最重要的供应商&#xff0c;并在论文中列表…

钢轨缺陷检测-目标检测数据集(包括VOC格式、YOLO格式)

钢轨缺陷检测-目标检测数据集&#xff08;包括VOC格式、YOLO格式&#xff09; 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1h7Dc0MiiRgtd7524cBUOFQ?pwdfr9y 提取码&#xff1a;fr9y 数据集信息介绍&#xff1a; 共有 1493 张图像和一一对应的标注文件 标…

Neo4j入门案例:三星堆

创建一个关于三星堆的知识图谱可以是一个非常有趣的项目&#xff0c;它可以帮助理解如何使用Neo4j来存储和查询复杂的关系数据。三星堆文化以其独特的青铜器、金器和其他文物而闻名&#xff0c;这为我们提供了一个丰富的历史背景来构建知识图谱。 数据模型定义 实体类型&#…

RTMP直播播放器的几种选择

如何选择RTMP播放器&#xff1f; 在选择RTMP播放器时&#xff0c;需要综合考虑多个因素&#xff0c;以确保选择的播放器能够满足实际需求并提供良好的用户体验。以下是一些选择RTMP播放器的建议&#xff1a; 1. 功能需求 低延迟&#xff1a;对于直播场景&#xff0c;低延迟是…

解读 Java 经典巨著《Effective Java》90条编程法则,第5条:优先考虑依赖注入来引用资源

【前言】欢迎订阅【解读《Effective Java》】系列专栏 《Effective Java》是 Java 开发领域的经典著作&#xff0c;作者 Joshua Bloch 以丰富的经验和深入的知识&#xff0c;全面探讨了 Java 编程中的最佳实践。这本书被公认为 Java 开发者的必读经典&#xff0c;对提升编码技…

STM32巡回研讨会总结(2024)

前言 本次ST公司可以说是推出了7大方面&#xff0c;几乎可以说是覆盖到了目前生活中的方方面面&#xff0c;下面总结下我的感受。无线类 支持多种调制模式&#xff08;LoRa、(G)FSK、(G)MSK 和 BPSK&#xff09;满足工业和消费物联网 (IoT) 中各种低功耗广域网 (LPWAN) 无线应…

MelosBoom:解锁数据价值的新纪元

在当今的数字时代&#xff0c;数据被誉为“新的石油”&#xff0c;但用户在传统的Web2环境中&#xff0c;往往无法真正享受到自己贡献数据的价值。大型互联网公司通过集中化的系统和算法&#xff0c;垄断了数据的使用权&#xff0c;并从中获取巨大的商业利益&#xff0c;而数据…

计算机三级网络技术总结(一)

RPR环中每一个节点都执行SRP公平算法IEEE 802.11a和g将传输速率提高到54Mbps一个BGP发言人与其他自治系统中的BGP发言人要交换路由信息就要先建立TCP连接在一个区域内的路由器数一般不超过200个进入接口配置模式&#xff1a;Router(config)#interface <接口名> 封装ppp协…