【困难】力扣算法题解析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 作为栈,操作高效;链表头部添加避免反转操作;最小堆确保字典序最小,且堆操作高效。


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

相关推荐
卓怡学长5 分钟前
m315基于java的水果网上商城的开发与设计
java·数据库·spring·tomcat·maven·intellij-idea
wuweijianlove30 分钟前
算法复杂度估算的实验建模与可视化表达的技术6
算法
执笔画流年呀31 分钟前
7大排序算法
java·算法·排序算法
AI成长日志36 分钟前
【算法学习专栏】动态规划基础·中等两题精讲(198.打家劫舍、322.零钱兑换)
学习·算法·动态规划
计算机安禾39 分钟前
【数据结构与算法】第28篇:平衡二叉树(AVL树)
开发语言·数据结构·数据库·线性代数·算法·矩阵·visual studio
测试_AI_一辰39 分钟前
AI 如何参与 Playwright 自动化维护:一次自动修复闭环实践
人工智能·算法·ai·自动化·ai编程
未来之窗软件服务1 小时前
算法设计—计算机等级考试—软件设计师考前备忘录—东方仙盟
算法·软件设计师·计算机等级考试
未来之窗软件服务1 小时前
哈夫曼树构造—计算机等级考试—软件设计师考前备忘录—东方仙盟
算法·软件设计师·计算机等级考试·仙盟创梦ide·东方仙盟
zdl6861 小时前
springboot+全局异常处理
java·spring boot·spring
2301_771717211 小时前
Jackson的使用方法详解
java·服务器·前端