【困难】力扣算法题解析LeetCode332:重新安排行程

关注文末推广名片,即可免费获得本题测试源码

题目来源:LeetCode332:重新安排行程

问题抽象: 给定机票列表 ticketstickets[i] = [from_i, to_i] 表示从 from_i 机场飞往 to_i 机场的行程),要求 重建行程路线 使其满足以下核心需求:

  1. 行程规则

    • 起点固定 :路线必须从 "JFK" 机场开始;
    • 全覆盖 :所有机票 必须被使用且仅使用一次(每条边遍历一次);
    • 字典序最小 :若存在多条可行路线,返回 字典序最小 的行程(如 ["JFK","ABC"] 优先于 ["JFK","ACB"])。
  2. 输入约束

    • 机票数量 ∈ [1, 300]
    • 机场代码为 3个大写字母 (如 "JFK", "SFO");
    • 保证至少存在一条可行路线(无需处理无解场景)。
  3. 图结构特性

    • 机票表示 有向边from_i → to_i);
    • 路线需形成 欧拉路径(所有边遍历一次);
    • 可能存在 多重边(同一航线多张机票)。
  4. 输出要求

    • 返回机场代码列表(按行程顺序排列);
    • 示例:
      • tickets=[["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]
        ["JFK","MUC","LHR","SFO","SJC"]
      • tickets=[["JFK","KUL"],["JFK","NRT"],["NRT","JFK"]]
        ["JFK","NRT","JFK","KUL"](字典序最小)。
  5. 关键挑战

    • 字典序优化 :每个机场的下一站需按字典序排序(优先选 "ABC" 而非 "ACB");
    • 环检测 :需处理 局部环 (如 JFK→NRT→JFK→KUL);
    • 多重边消耗:同航线多张机票需独立消耗。

输入 :二维字符串数组 tickets(如 [["JFK","SFO"]]
输出:字符串列表(完整行程路线)。


解题思路

题目本质是寻找有向图的欧拉路径(Eulerian Path),即一条访问图中每条边恰好一次的路径。要求从 JFK 出发,且返回字典序最小的行程。Hierholzer 算法可高效求解欧拉路径,结合贪心策略(每次选择字典序最小的邻居)确保结果字典序最小。

关键步骤:

  1. 建图:

    • 使用 HashMap 存储每个出发机场对应的到达机场列表。
    • 为每个出发机场的到达机场列表使用最小堆(PriorityQueue),确保每次取字典序最小的邻居。
  2. Hierholzer 算法(非递归实现):

    • 栈(Stack): 模拟 DFS 过程,存储当前路径节点。
    • 链表(LinkedList): 存储最终结果,每次将没有出边的节点加入链表头部。
    • 算法流程:
      1. 将起点 "JFK" 压入栈。
      2. 当栈非空时:
        • 取栈顶节点 cur(不弹出)。
        • cur 存在邻居(即优先队列非空),弹出最小邻居并压入栈。
        • cur 无邻居,弹出 cur 并加入结果链表头部。
      3. 返回结果链表。

时间复杂度: O(n log n),其中 n 为边数。建图 O(n),Hierholzer 算法中每条边访问一次,堆操作 O(log n)。空间复杂度: O(n),存储图、栈和结果。


代码实现(Java版)🔥点击下载源码

java 复制代码
class Solution {
    public List<String> findItinerary(List<List<String>> tickets) {
        // 构建图:出发机场 -> 到达机场的最小堆
        Map<String, PriorityQueue<String>> graph = new HashMap<>();
        for (List<String> ticket : tickets) {
            String from = ticket.get(0);
            String to = ticket.get(1);
            graph.putIfAbsent(from, new PriorityQueue<>());
            graph.get(from).offer(to);
        }
        
        // 使用栈进行 DFS 模拟
        Deque<String> stack = new ArrayDeque<>();
        LinkedList<String> result = new LinkedList<>(); // 结果链表(头部添加)
        stack.push("JFK"); // 起点入栈
        
        while (!stack.isEmpty()) {
            String cur = stack.peek();
            // 当前节点还有邻居
            if (graph.containsKey(cur) && !graph.get(cur).isEmpty()) {
                String next = graph.get(cur).poll(); // 取最小邻居
                stack.push(next);
            } else {
                // 无邻居时,弹出节点并加入结果头部
                result.addFirst(stack.pop());
            }
        }
        return result;
    }
}

代码说明

  1. 建图:

    • graph 使用 HashMap,键为出发机场,值为到达机场的 PriorityQueue(最小堆)。
    • 遍历 tickets,将每个出发机场对应的到达机场加入最小堆,保证字典序。
  2. Hierholzer 算法:

    • 栈 (stack): 存储当前访问路径,起点 "JFK" 入栈。
    • 结果链表 (result): 使用 LinkedList 支持头部插入。
    • 循环处理:
      • 若栈顶节点有邻居,弹出最小邻居并入栈。
      • 若栈顶节点无邻居,弹出节点并加入 result 头部(后序加入)。
    • 最终 result 即为欧拉路径(正序)。
  3. 正确性保证:

    • 最小堆确保每次选择字典序最小的邻居。
    • 后序加入结果确保路径顺序正确(无出边的节点为路径终点)。

亮点: 使用 ArrayDeque 作为栈,操作高效;链表头部添加避免反转操作;最小堆确保字典序最小,且堆操作高效。


提交详情(执行用时、内存消耗)

相关推荐
Sagittarius_A*2 小时前
后量子密码|通识认知 03|量子威胁辟谣:Grover 算法对对称密码的影响与误区
算法·信息安全·密码学·量子计算·pqc·后量子密码
Patrick在香港2 小时前
Python Docker镜像从1.2GB到89MB:多阶段构建的完整优化实录
java·python·docker·信息可视化·容器·数据分析·ai编程
上海安当技术2 小时前
敏感数据怎么防拖库?信封加密(DEK+KEK 二层密钥)架构设计与 Java 实战
java·开发语言·python
我是唐青枫3 小时前
Java JCommander 实战详解:用注解解析命令行参数
java·开发语言
大鹏说大话3 小时前
C++ 内存布局详解:类、虚函数、虚表底层原理
java·开发语言
还是鼠鼠3 小时前
Spring AI连接DeepSeek与通义千问:application.yaml配置详解
java·通义千问·spring ai·deepseek
QAQo7T4 小时前
Python组蓝桥杯备赛超详细知识点总结笔记_排序算法篇
笔记·python·算法·蓝桥杯·排序算法
千里之行,始于足下sanhai4 小时前
P8686 [蓝桥杯 2019 省 A] 修改数组 - 数字魔法大冒险 题解
c++·算法·动态规划
小星星闪亮登场4 小时前
Codeforces Round 1115 ( Div. 2)
数据结构·c++·算法·贪心算法·排序算法·codeforces
计算机小白一个4 小时前
蓝桥杯 Java B 组之哈希表应用(两数之和、重复元素判断)
java·数据结构·算法·蓝桥杯