一、题目深度解析与行程规划本质

题目描述

给定一个机票的字符串二维数组 tickets,每个元素是 [from, to] 的形式,表示从 fromto 的机票。要求找出从 JFK 出发的行程,且必须使用所有机票,若存在多种可能的行程,返回字典序最小的那个。

核心特性分析

  1. 图论模型:每个机场是图的节点,机票是图的边,问题转化为在图中寻找一条经过所有边的路径
  2. 欧拉路径:题目本质是寻找图中的欧拉路径(经过每条边恰好一次的路径)
  3. 字典序要求:在可能的路径中选择字典序最小的

示例说明

  • 输入:[["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]
  • 输出:["JFK","MUC","LHR","SFO","SJC"]

二、代码核心实现与数据结构设计

完整代码实现

class Solution {Map<String, PriorityQueue<String>> targetMap = new HashMap<>(); // 存储起点到终点的映射,用优先队列保持字典序List<String> result = new LinkedList<>(); // 存储最终行程public List<String> findItinerary(List<List<String>> tickets) {// 构建邻接表,使用优先队列保证字典序for (List<String> ticket : tickets) {String start = ticket.get(0);String end = ticket.get(1);if (!targetMap.containsKey(start)) {targetMap.put(start, new PriorityQueue<>());}targetMap.get(start).offer(end);}dfs("JFK"); // 从JFK开始DFSCollections.reverse(result); // 反转结果得到正确顺序return result;}public void dfs(String current) {// 当当前机场还有未使用的机票时,继续递归while (targetMap.containsKey(current) && targetMap.get(current).size() > 0) {dfs(targetMap.get(current).poll());}// 当前机场没有未使用的机票时,加入结果result.add(current);}
}

核心数据结构解析:

  1. targetMap

    • 键:起点机场
    • 值:PriorityQueue存储终点机场,自动按字典序排序
    • 作用:构建邻接表,确保每次选择字典序最小的终点
  2. result

    • 存储最终行程的列表
    • 注意:结果需要反转,原因后文详细解释
  3. DFS函数

    • 递归处理每个机场的行程
    • 后序遍历方式确保机票被完全使用

三、核心问题解析:优先队列与DFS的协同作用

1. 为什么使用优先队列?

字典序保证:

PriorityQueue的自然排序特性确保:

  • 当同一个起点有多个终点时,优先选择字典序最小的终点
  • 例如:起点"JFK"对应终点[“SFO”, “ATL”],优先队列会先选"ATL"
代码体现:
targetMap.get(start).offer(end); // 按字典序插入
targetMap.get(current).poll(); // 取出字典序最小的终点

2. DFS的后序遍历逻辑

遍历过程:
while (targetMap.containsKey(current) && targetMap.get(current).size() > 0) {dfs(targetMap.get(current).poll());
}
result.add(current);
  • 先递归后添加:先处理所有子节点,再将当前节点加入结果
  • 后序遍历原因
    1. 确保所有子行程处理完毕后,再将当前机场加入行程
    2. 类似于"填坑"策略,确保每条边都被使用
示例说明:
  • 行程路径:JFK→MUC→LHR→SFO→SJC
  • DFS顺序:
    1. 递归到SJC(最末端节点)
    2. 返回时添加SJC到结果
    3. 递归到SFO,添加SFO
    4. 依此类推,最终结果需要反转

3. 为什么反转结果?

后序遍历的反转需求:
  • DFS过程是后序遍历(先处理子节点,再处理父节点)
  • 例如:遍历顺序为 SJC→SFO→LHR→MUC→JFK
  • 反转后得到正确的行程顺序:JFK→MUC→LHR→SFO→SJC
代码体现:
Collections.reverse(result); // 反转后序遍历结果

四、DFS流程深度模拟:以示例输入为例

示例输入:

[["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]

构建的targetMap:

  • JFK: [“MUC”]
  • MUC: [“LHR”]
  • LHR: [“SFO”]
  • SFO: [“SJC”]

DFS执行过程:

  1. 调用dfs(“JFK”)

    • JFK存在且有终点MUC,poll出MUC,递归dfs(“MUC”)
  2. 调用dfs(“MUC”)

    • MUC存在且有终点LHR,poll出LHR,递归dfs(“LHR”)
  3. 调用dfs(“LHR”)

    • LHR存在且有终点SFO,poll出SFO,递归dfs(“SFO”)
  4. 调用dfs(“SFO”)

    • SFO存在且有终点SJC,poll出SJC,递归dfs(“SJC”)
  5. 调用dfs(“SJC”)

    • SJC不存在于targetMap或没有终点,添加"SJC"到result,返回
  6. 回到dfs(“SFO”)

    • 添加"SFO"到result,返回
  7. 回到dfs(“LHR”)

    • 添加"LHR"到result,返回
  8. 回到dfs(“MUC”)

    • 添加"MUC"到result,返回
  9. 回到dfs(“JFK”)

    • 添加"JFK"到result,返回

结果列表:

  • 初始result: [“SJC”, “SFO”, “LHR”, “MUC”, “JFK”]
  • 反转后: [“JFK”, “MUC”, “LHR”, “SFO”, “SJC”]

五、算法复杂度分析

1. 时间复杂度

  • O(E log E)
    • E为机票数量(边数)
    • 构建优先队列:每个边入队出队操作O(log E)
    • DFS遍历:每个边处理一次O(E)

2. 空间复杂度

  • O(E)
    • 邻接表存储所有边O(E)
    • 结果列表存储所有节点O(E+1)

六、核心技术点总结:行程规划的关键要素

1. 图模型构建

  • 邻接表表示:使用HashMap+PriorityQueue构建有向图
  • 字典序保证:PriorityQueue确保每次选择字典序最小的边

2. 后序DFS遍历

  • 遍历策略:先递归处理子节点,再将当前节点加入结果
  • 反转需求:后序遍历结果需要反转得到正确行程顺序

3. 欧拉路径特性

  • 问题本质:寻找图中的欧拉路径
  • 算法保证:DFS后序遍历确保每条边被访问一次

七、常见误区与优化建议

1. 忽略PriorityQueue的作用

  • 误区:使用普通List存储终点,无法保证字典序
    Map<String, List<String>> targetMap = new HashMap<>(); // 错误,无法保证顺序
    
  • 正确做法:使用PriorityQueue保证字典序

2. 忘记反转结果

  • 误区:直接返回后序遍历结果
    return result; // 错误,顺序相反
    
  • 正确做法:反转后返回

3. 优化建议:处理重复机票

// 处理重复机票的情况(如存在多条相同航线)
// 代码已天然支持,PriorityQueue会按顺序处理
  • 优势:PriorityQueue自动处理重复边的字典序选择
  • 注意:题目保证存在有效行程,无需额外处理

八、总结:优先队列与后序DFS的完美结合

本算法通过优先队列和后序DFS的结合,高效解决了行程规划问题,核心在于:

  1. 数据结构选择:PriorityQueue保证字典序,HashMap构建邻接表
  2. 遍历策略:后序DFS确保所有边被使用,反转结果得到正确顺序
  3. 问题建模:将行程问题转化为图的欧拉路径问题

理解这种解法的关键是把握后序遍历与反转操作的配合,以及优先队列在字典序保证中的作用。这种思路不仅适用于行程规划,还可迁移到其他图的欧拉路径问题,是图论算法在实际场景中的典型应用。

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

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

相关文章

1.21SQLCipher 简介

SQLCipher 是一个基于 SQLite 的扩展&#xff0c;提供了透明的数据库加密功能。与普通 SQLite 不同&#xff0c;SQLCipher 在数据写入磁盘前自动加密&#xff0c;读取时自动解密&#xff0c;无需开发者手动处理加密逻辑。这使得它非常适合移动应用、桌面应用等需要本地数据加密…

无人机不再“盲飞”!用Python搞定实时目标识别与跟踪

友友们好! 我是Echo_Wish,我的的新专栏《Python进阶》以及《Python!实战!》正式启动啦!这是专为那些渴望提升Python技能的朋友们量身打造的专栏,无论你是已经有一定基础的开发者,还是希望深入挖掘Python潜力的爱好者,这里都将是你不可错过的宝藏。 在这个专栏中,你将会…

Vue-7-前端框架Vue之应用基础从Vue2语法到Vue3语法的演变

文章目录 1 基于vite创建1.1 对比webpack和vite1.2 创建工程1.3 启动项目2 调试工具Vue.js Devtools3 src结构3.1 index.html3.2 main.ts3.3 App.vue(根组件)4 示例(Vue2的语法)4.1 Person.vue4.2 App.vue4.3 选项式API对比组合式API4.4 程序流程5 示例(Vue3的语法)5.1 setup概…

上线iOSApp前抓包工具协作保障接口行为一致性(iOS抓包)

项目上线前&#xff0c;你是否总会担心“接口是不是在某个边缘条件下表现不一致”&#xff1f;哪怕单元测试通过、接口文档齐全&#xff0c;真到线上用户手上&#xff0c;总还是可能出现一些环境相关的异常。 最近参与某App大版本上线前的质量验证流程&#xff0c;我们特别安排…

Java可变参数:灵活编程的秘密武器

Java可变参数的理解与应用 Java中的可变参数&#xff08;Varargs&#xff09;允许方法接受数量不定的同类型参数&#xff0c;简化了方法调用时的参数传递。可变参数通过在参数类型后添加...实现&#xff0c;本质上是一个数组&#xff0c;但在调用时可以传入多个单独的参数。 …

汽车 CDC威胁分析与风险评估

汽车 CDC&#xff08;连续阻尼控制系统&#xff09;的威胁分析与风险评估需结合其技术特性、应用场景及行业标准展开。以下是详细解析及实例说明&#xff1a; 一、CDC 系统技术原理与结构 CDC&#xff08;Continuous Damping Control&#xff09;通过实时调节悬挂阻尼力提升驾…

TensorFlow 安装与 GPU 驱动兼容(h800)

环境说明TensorFlow 安装与 GPU 驱动兼容CUDA/H800 特殊注意事项PyCharm 和终端环境变量设置方法测试 GPU 是否可用的 Python 脚本 # 使用 TensorFlow 2.13 在 NVIDIA H800 上启用 GPU 加速完整指南在使用 TensorFlow 进行深度学习训练时&#xff0c;充分利用 GPU 能力至关重要…

Laravel 项目中图片上传后无法访问的问题

情况&#xff1a; Laravel 提供了 php artisan storage:link 命令&#xff0c;用于创建符号链接&#xff08;Symbolic Link&#xff09;&#xff0c;将 storage/app/public 映射到 public/storage。但是上传图片之后 文件目录确实有 但是无法访问。 1. 删除已经创建的 rm -rf…

Tesollo携人形机器人手进军国内市场

Tesollo灵巧手是Tesollo公司研发的一系列机器人灵巧手产品&#xff0c;涵盖两指到五指的设计 产品型号与特点 Delto-5F五指灵巧手&#xff1a;具备20个自由度&#xff0c;每个手指配备4个独立关节&#xff0c;抓握力达到7公斤&#xff0c;每个关节空载可达75转/分钟&#xff0…

Python文件操作的“保险箱”:with语句深度实战指南

目录 一、with语句的底层运作原理 资源获取阶段 资源释放阶段 二、文件操作实战场景解析 场景1:基础文件读写 场景2:异常处理进阶 场景3:复合资源管理 三、自定义上下文管理器 四、with语句的性能考量 五、实战经验总结 在Python编程中,文件操作是日常开发的高频…

openKylin高校沙龙 | 走进成都高校,推动开源技术交流与人才培养

openKylin高校沙龙 | 成都高校 4月25日&#xff0c;CCF开源发展委员会“开源高校行”暨红山开源openKylin高校行成都站圆满举办&#xff0c;这场连接两所大学的开源知识盛会&#xff0c;为成都信息工程大学与电子科技大学的300余名与会师生带来了前沿的行业思考与技术实践。Op…

即梦3.0更新后市面上的的评价如何?

设计师紧握数位板缩在墙角&#xff0c;全息投影中的AI正在生成同风格设计图&#xff0c;地面倒影显示“人类设计师生涯倒计时”。当最新一代AI绘图工具悄然开启测试时&#xff0c;设计圈陷入集体震动——有人惊叹“以后还干XX&#xff0c;都回家卖煎饼吧”&#xff0c;也有人彻…

haproxy搭建nginx网站访问

文章目录 一.案例概述2.1 HTTP请求2.2 负载均衡常用调度算法①RR&#xff08;Round robin&#xff09;②LC&#xff08;least connections&#xff09;③SH&#xff08;source hashing&#xff09; 2.3 常见的web群集调度器3.实验环境 二.实验步骤1.两台web网站步骤相同 安装we…

进程间通信之socketpair

进程间通信之socketpair 源代码 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h>int main() {//父子通讯管道int m_pipe[2];//创建管道if(socketpa…

跟着AI学习C# Day29

&#x1f4c5; Day 29&#xff1a;C# 综合进阶知识回顾与职业发展建议 ✅ 学习目标&#xff1a; 回顾 C# 进阶学习路径&#xff1b;总结核心知识点&#xff0c;构建完整的技能体系&#xff1b;理解 C# 高级开发者应具备的核心能力&#xff1b;探索 C# 在不同技术领域的应用场…

茶席布置实训室:传承与创新的茶文化空间

一、茶席布置实训室的重要意义 茶席布置实训室是茶文化传承与创新的重要载体。在现代社会&#xff0c;茶文化的弘扬不仅是对传统的尊重&#xff0c;更是对生活品质和精神境界的追求。茶席布置实训室为人们提供了一个专业、系统地学习和实践茶文化的场所。它将理论知识与实际操…

jar is missing

在父POM中通过dependencyManagement统一管理版本&#xff0c;然后在子模块中省略版本号。

Linux 内核中 TCP 协议栈的输出实现:tcp_output.c 文件解析

在网络通信领域,TCP(传输控制协议)作为核心的传输层协议,确保了数据在网络中的可靠传输。Linux 内核中的 TCP 协议栈实现复杂而高效,其中 net/ipv4/tcp_output.c 文件是整个 TCP 协议栈的关键组成部分,负责处理数据包的发送、重传、连接管理等核心功能。本文将深入解析该…

MySQL分页原理与慢SQL优化实战

分页查询的本质 在Web应用中&#xff0c;分页是处理大量数据的常见需求。MySQL中的分页通常使用LIMIT offset, size语法实现&#xff0c;例如&#xff1a; SELECT * FROM users ORDER BY id LIMIT 10000, 20; 这条语句看似简单&#xff0c;但隐藏着性能陷阱。让我们深入理解…

Taro:跨端开发的终极解决方案

在当今多终端并存的互联网时代&#xff0c;开发者经常面临一个难题&#xff1a;如何高效地为不同平台&#xff08;如微信小程序、H5、React Native 等&#xff09;开发功能一致的应用&#xff1f;传统的开发方式需要针对每个平台单独编写代码&#xff0c;不仅效率低下&#xff…