算法训练营day58 图论⑧ 拓扑排序精讲、dijkstra(朴素版)精讲

        本篇应该是图论的经典部分了,本篇的内容作为小白没有了解过,但是至少会听说过——拓扑排序精讲、dijkstra(朴素版)精讲。

拓扑排序精讲

        本题是拓扑排序的经典题目。一聊到 拓扑排序,一些录友可能会想这是排序,不会想到这是图论算法。其实拓扑排序是经典的图论问题。

        给出一个 有向图,把这个有向图转成线性的排序 就叫拓扑排序。当然拓扑排序也要检测这个有向图 是否有环,即存在循环依赖的情况,因为这种情况是不能做线性排序的。所以拓扑排序也是图论中判断有向无环图的常用方法

        拓扑排序指的是一种 解决问题的大体思路, 而具体算法,可能是广搜也可能是深搜。实现拓扑排序的算法有两种:卡恩算法(BFS)和DFS。一般来说我们只需要掌握 BFS (广度优先搜索)就可以了,清晰易懂,如果还想多了解一些,可以再去学一下 DFS 的思路,但 DFS 不是本篇重点。

        当我们做拓扑排序的时候,应该优先找 入度为 0 的节点,只有入度为0,它才是出发节点。 理解以上内容很重要

        接下来我给出 拓扑排序的过程,其实就两步:

  1. 找到入度为0 的节点,加入结果集
  2. 将该节点从图中移除(从头开始循环)

        结果集的顺序,就是我们想要的拓扑排序顺序 (结果集里顺序可能不唯一)

判断有环

        那么如果我们发现结果集元素个数 不等于 图中节点个数,我们就可以认定图中一定有 有向环!这也是拓扑排序判断有向环的方法。

代码实现

        并不需要一定要把节点删除,重点在于节点入度的判断,只要相关节点入度-1即可,重点在于对于节点入度的处理,以及通过队列结构对于节点的遍历,类似我们之前做到过的二叉树的层序遍历

# 导入必要的模块
# deque:用于实现队列(高效的弹出左侧元素操作)
# defaultdict:用于构建邻接表(默认值为列表,简化键不存在时的处理)
from collections import deque, defaultdictdef topological_sort(n, edges):"""拓扑排序函数:对有向无环图(DAG)进行拓扑排序,常用于解决依赖关系问题(如文件依赖、任务调度)参数:n: 节点总数(如文件数量)edges: 边的列表,每个元素为 tuple(s, t),表示存在一条从 s 到 t 的有向边(s 依赖 t 或 t 依赖 s,根据场景而定)功能:输出拓扑排序结果;若图中存在环,则输出 -1"""# inDegree 数组:记录每个节点的入度(即有多少个前置依赖)# 索引代表节点编号,值代表入度数量inDegree = [0] * n# umap:邻接表,记录节点间的依赖关系# key 是起始节点,value 是 key 指向的所有节点(列表)umap = defaultdict(list)# 构建图(邻接表)和入度表for s, t in edges:# s -> t 表示:要完成 t 必须先完成 s(或 t 依赖 s)# 因此 t 的入度 +1(增加一个前置依赖)inDegree[t] += 1# 在邻接表中记录 s 指向 t(s 完成后可处理 t)umap[s].append(t)# 初始化队列:将所有入度为 0 的节点加入队列# 入度为 0 表示该节点没有前置依赖,可以直接开始处理queue = deque([i for i in range(n) if inDegree[i] == 0])# 存储拓扑排序的结果result = []# 队列不为空时,循环处理节点while queue:# 取出队列左侧的节点(当前可处理的节点)cur = queue.popleft()# 将当前节点加入结果列表result.append(cur)# 遍历当前节点指向的所有节点(即依赖当前节点的节点)for file in umap[cur]:# 依赖当前节点的节点,其入度减 1(少了一个前置依赖)inDegree[file] -= 1# 若入度减为 0,说明该节点的所有前置依赖都已处理完,可加入队列等待处理if inDegree[file] == 0:queue.append(file)# 若结果列表长度等于节点总数,说明所有节点都被处理(图无环),输出排序结果if len(result) == n:print(" ".join(map(str, result)))# 否则,说明图中存在环(部分节点因入度无法减为 0 而未被处理),输出 -1else:print(-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(朴素版)精讲

        dijkstra算法:在有权图(权值非负数)中求从起点到其他节点的最短路径算法。

        需要注意两点:

  • dijkstra 算法可以同时求 起点到所有节点的最短路径
  • 权值不能为负数

 dijkstra三部曲

  1. 第一步,选源点到哪个节点近且该节点未被访问过
  2. 第二步,该最近节点被标记访问过
  3. 第三步,更新非访问节点到源点的距离(即更新minDist数组)

        与prim算法基本一致,区别在于 dijkstra算法是一个积累的过程对数组赋值,而prim是当下节点的值没有累加。即:prim是求 非访问节点到最小生成树的最小距离,而 dijkstra是求 非访问节点到源点的最小距离

        prim算法 可以有负权值吗?当然可以!prim算法只需要将节点以最小权值和链接在一起,不涉及到单一路径。

对于负值

        该算法不能接受权值为负数(对应算法为Bellman-Ford 算法,后续讲),因为权值存在负数会对节点循环造成影响,需要多次遍历已经访问过的节点(之前是每个节点只会遍历一次),会造成循环结构的困扰

import sysdef dijkstra(n, m, edges, start, end):"""Dijkstra算法实现:求解从起点到终点的最短路径参数:n: 节点总数m: 边的数量edges: 边的列表,每个元素为元组(p1, p2, val),表示从p1到p2的有向边,权重为valstart: 起点编号end: 终点编号返回:起点到终点的最短路径长度;若无法到达,返回-1"""# 初始化邻接矩阵,用于存储节点间的距离# 初始值设为无穷大(float('inf')),表示初始时节点间不可达# 矩阵大小为(n+1)x(n+1),因为节点编号从1开始grid = [[float('inf')] * (n + 1) for _ in range(n + 1)]# 填充邻接矩阵:根据边的信息设置节点间的距离for p1, p2, val in edges:grid[p1][p2] = val  # 有向边,仅设置p1到p2的距离# minDist数组:记录从起点到每个节点的最短距离# 初始值设为无穷大,表示尚未确定距离minDist = [float('inf')] * (n + 1)# visited数组:标记节点是否已被访问(即是否已确定最短路径)visited = [False] * (n + 1)# 起点到自身的距离为0minDist[start] = 0# Dijkstra算法主循环:需要遍历所有节点(最多n次)for _ in range(1, n + 1):# 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 = v              # 更新选中的节点# 如果找不到可访问的节点(所有可达节点已处理),提前结束循环if cur == -1:break# 2. 标记当前节点为已访问(其最短路径已确定)visited[cur] = True# 3. 更新所有未访问节点通过当前节点到达起点的距离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]:# 更新最短距离minDist[v] = minDist[cur] + grid[cur][v]# 返回结果:若终点不可达(距离仍为无穷大),返回-1;否则返回最短距离return -1 if minDist[end] == float('inf') else minDist[end]if __name__ == "__main__":"""程序入口:读取输入并调用Dijkstra算法计算结果"""# 一次性读取所有输入数据input = sys.stdin.readdata = input().split()  # 按空白字符分割成列表# 解析节点总数和边数n, m = int(data[0]), int(data[1])# 解析所有边的信息edges = []index = 2  # 从第三个元素开始是边的信息for _ in range(m):p1 = int(data[index])p2 = int(data[index + 1])val = int(data[index + 2])edges.append((p1, p2, val))  # 将边信息存入列表index += 3  # 移动到下一条边的起始位置# 设定起点为1,终点为n(可根据实际需求修改)start = 1end = n# 调用Dijkstra算法计算最短路径并输出结果result = dijkstra(n, m, edges, start, end)print(result)

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

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

相关文章

如何在日常开发中高效使用 Copilot

网罗开发&#xff08;小红书、快手、视频号同名&#xff09;大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等方…

使用Docker部署Coze Studio开源版

1、安装Docker# 安装Docker https://docs.docker.com/get-docker/# 安装Docker Compose https://docs.docker.com/compose/install/# CentOS安装Docker https://mp.weixin.qq.com/s/nHNPbCmdQs3E5x1QBP-ueA2、安装Coze Studio详见&#xff1a;https://github.com/coze-dev/coze…

深度剖析Spring AI源码(九):构建企业知识库,深入ETL与RAG实现

深度剖析Spring AI源码&#xff08;九&#xff09;&#xff1a;构建企业知识库&#xff0c;深入ETL与RAG实现 “Data is the new oil, but like oil, it’s valuable only when refined.” —— 在AI时代&#xff0c;原始数据需要经过精心的ETL处理才能成为AI的"燃料"…

C# 简单工厂模式:构建灵活与可扩展的面向对象程序

在面向对象编程&#xff08;OOP&#xff09;的世界中&#xff0c;简单工厂模式&#xff08;Simple Factory Pattern&#xff09; 是一种非常常见且实用的设计模式。虽然它并不属于GoF&#xff08;Gang of Four&#xff09;定义的23种经典设计模式之一&#xff0c;但它是理解更复…

全面解析JVM预热:原理、价值与实践指南

在Java应用的性能优化领域,“JVM预热”是一个常被提及却容易被忽视的关键环节。尤其是在高并发、低延迟的业务场景中,未经过充分预热的JVM可能导致应用启动初期出现响应延迟、吞吐量波动甚至服务不可用的问题。本文将从JVM预热的核心原理出发,深入剖析其价值、常见实现方案及…

数学建模-灰色关联分析(GRA)

目录 1-AI带你认识GRA &#x1f4d8; 一、灰色关联分析&#xff08;GRA&#xff09;简介 1. 什么是灰色关联分析&#xff1f; 2. 核心思想&#xff08;通俗理解&#xff09;&#xff1a; 3. 与熵权法的对比&#xff08;快速类比&#xff09;&#xff1a; &#x1f9e9; 二…

Shell脚本-expect

一、前言在 Linux 系统管理与自动化运维中&#xff0c;我们经常需要编写 Shell 脚本来完成各种任务。但有些命令&#xff08;如 ssh、scp、passwd、ftp 等&#xff09;在执行时会等待用户手动输入密码或确认信息&#xff0c;这就导致脚本无法完全自动化运行。为了解决这个问题&…

Conmi的正确答案——Ubuntu24.04禁用任何休眠

系统&#xff1a;Ubuntu 24.04步骤一、禁用系统休眠服务 # 禁用所有休眠/待机相关服务&#xff08;立即生效&#xff09; sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target # 验证状态&#xff08;显示 "masked" 即成功&am…

开源 C++ QT Widget 开发(二)基本控件应用

文章的目的为了记录使用C 进行QT Widget 开发学习的经历。临时学习&#xff0c;完成app的开发。开发流程和要点有些记忆模糊&#xff0c;赶紧记录&#xff0c;防止忘记。 相关链接&#xff1a; 开源 C QT Widget 开发&#xff08;一&#xff09;工程文件结构-CSDN博客 开源 C…

今日科技风向|从AI芯片定制到阅兵高科技展示——聚焦技术前沿洞察

今日科技风向&#xff5c;从AI芯片定制到阅兵高科技展示——聚焦技术前沿洞察 一、NVIDIA 开发“黑曜”子版 AI 芯片 B30A&#xff0c;瞄准中国市场 今日报道指出&#xff0c;NVIDIA 正在研发一款面向中国市场的定制芯片 B30A&#xff0c;基于其先进的 Blackwell 架构&#xff…

Elasticsearch官方文档学习-未完待续

Elasticsearch官方文档学习-未完待续说明快速开始基础知识索引组成1. 文档 (Documents)2. 元数据字段(Metadata fields)3. 映射和数据类型(Mappings and data types)文档操作(Document)批量创建或者删除文档 (Bulk index or delete documents)乐观并发控制 Optimistic concurre…

Redis资料

Redis是什么&#xff1f; Redis(Remote Dictionary Server)是一个开源的、基于内存的键值数据库&#xff0c;支持多种数据结构&#xff0c;可用作数据库、缓存和消息中间件。主要特点包括&#xff1a; 基于内存操作&#xff0c;读写性能极高支持持久化&#xff0c;可将内存数…

CAMEL-Task2-Agent的构成组件

CAMEL-Task2-Agent的构成组件 本文笔记主要关于2.7章节&#xff1a;CAMEL框架中agents如何使用工具。 一、工具说明 为什么要额外使用工具&#xff1f; agents本身的知识和能力有限&#xff0c;比如一些问题需要联网搜索才能准确回答&#xff08;而不是乱答&#xff0c;即“…

数据整理自动化 - 让AI成为你的数据助手

文章目录数据整理自动化 - 让AI成为你的数据助手引言&#xff1a;数据整理的时代挑战与机遇1. 常见数据整理场景分析1.1 数据整理的多元场景图谱1.2 数据质量问题的分类与影响1.3 传统处理方法的局限性2. AI与传统脚本的协同工作流2.1 智能数据整理架构设计2.2 协同工作流的最佳…

react速成

项目目录package.json文件&#xff1a;包含核心两个依赖&#xff08;react、react-dom&#xff09;和命令&#xff08;start、bulid&#xff09;src&#xff1a;源码目录&#xff0c;开始之用的到index.js和App.jsindex.js&#xff1a;是项目的入口&#xff0c;一切的运行起点/…

Maven的进阶使用(上)

pom.xml文件 就像 Make 的 MakeFile、Ant 的 build.xml 一样&#xff0c;Maven 项目的核心是 pom.xml。POM(全称 Project Object Model&#xff0c;项目对象模型 ) 定义了项目的基本信息&#xff0c;用于描述项目如何构建&#xff0c;声明项目依赖&#xff0c;等等。 Gredele--…

【最后203篇系列】034 使用SQLite构建简单的任务管理

表数据同步的断点续传 有时候需要将一个表的数据复制到另一个表&#xff0c;循环是常用的方式。当表比较大&#xff0c;执行的时间很长&#xff0c;会有很多因素引起失败。我希望可以比较简单的跑数&#xff0c;所以做一个简单的任务系统。 SQLitre是嵌入式数据库&#xff0c;这…

SpringCloud Alibaba核心知识点

Spring Cloud Alibaba 是阿里巴巴开源的一套微服务解决方案&#xff0c;与 Spring Cloud 生态深度集成。以下是其主要组件及其功能&#xff1a;Nacos服务注册与发现&#xff1a;支持动态服务注册、健康监测及DNS-Based服务发现。配置中心&#xff1a;提供分布式配置管理&#x…

LeetCode 分类刷题:34. 在排序数组中查找元素的第一个和最后一个位置

题目给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。示例 1&#xff1a;…

自建知识库,向量数据库 (十二)之 文章向量搜索——仙盟创梦IDE

“未来之窗” 文章向量搜索&#xff1a;多领域应用与学习指南 在数字化浪潮中&#xff0c;“未来之窗” 文章向量搜索凭借其独特的技术优势&#xff0c;在酒店、电商、诊疗及知识库等多个领域展现出巨大的应用潜力&#xff0c;为各行业的信息处理与检索带来了全新的视角和高效…