【LeetCode】332. 重新安排行程(困难)——代码随想录算法训练营Day30

题目链接:332. 重新安排行程

题目描述

给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。

所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

  • 例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前。

假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

示例 1:

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

示例 2:

输入:tickets = [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
输出:["JFK","ATL","JFK","SFO","ATL","SFO"]
解释:另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"] ,但是它字典排序更大更靠后。

提示:

  • 1 <= tickets.length <= 300
  • tickets[i].length == 2
  • fromi.length == 3
  • toi.length == 3
  • fromi 和 toi 由大写英文字母组成
  • fromi != toi

文章讲解:代码随想录

题解1:回溯法

思路:本题考虑到对有向图进行深度优先搜索得出答案,同时涉及到车票数量的问题,可以考虑使用回溯法。首先构建一个出发地、目的地和车票数量之间的映射,使用 Map<String, Map<String, int>> 的结构,即 Map<出发地, Map<目的地, 车票数量>>。然后从第1个出发地开始,深度优先遍历目的地,在对车票进行排序后,第一个找出的结果即为本题答案。

回溯分析:

  • 递归函数的参数和返回值:首先创建变量 res 和 path,res 用于存放结果,path 为路径。参数为空,返回一个布尔值,如果找到结果,就返回 true,否则返回 false。
  • 递归函数的终止条件:找到一个符合要求的结果,即路径的长度为车票总数量加1。
  • 单层递归的逻辑:以路径最后一个位置为出发地,横向遍历所有目的地,继续纵向向下遍历。
  • 剪枝:当出发地与一个目的地之间车票数量为0,则说明这个树枝没有结果,跳过这次遍历。
/**
 * @param {string[][]} tickets
 * @return {string[]}
 */
var findItinerary = function(tickets) {
    tickets.sort(); // 对车票排序
    // 构建映射关系,出发地: { 目的地: 车票数量 }
    const map = {};
    tickets.forEach(item => {
        map[item[0]] ? (map[item[0]][item[1]] ? map[item[0]][item[1]]++ : map[item[0]][item[1]] = 1) : map[item[0]] = { [item[1]]: 1 };
    });
    let res = []; // 保存结果
    const path = ["JFK"]; // 路径
    const backtracking = function () {
        // 路径长度等于车票数量加1,说明收集到了1个结果,因为原数组已排序,第1个结果就是本题的结果
        if (path.length === tickets.length + 1) {
            res = [...path]; // 记录到结果中
            return true; // 找到符合条件的路径,返回 true
        }
        const arr = map[path[path.length - 1]]; // 获取下一个目的地
        for (let i in arr) {
            // 当有车票时进行操作
            if (arr[i] > 0) {
                path.push(i); // 记录路径
                arr[i]--; // 更新车票数量
                // 向下查找
                if (backtracking()) {
                    return true; // 找到符合条件的路径,返回 true
                }
                // 回溯
                path.pop();
                arr[i]++;
            }
        };
        return false;
    }
    backtracking();
    return res;
};

收获

回溯法本质就是一个暴力的深度优先搜索算法,递归的遍历中带有回溯,不可能出现结果的部分应该剪枝。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晴雪月乔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值