LeetCode332.重新安排行程

164 阅读3分钟

「这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战

题目

给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。

提示:

  • 如果存在多种有效的行程,请你按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前
  • 所有的机场都用三个大写字母表示(机场代码)。
  • 假定所有机票至少存在一种合理的行程。
  • 所有的机票必须都用一次 且 只能用一次。

示例 1:

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

示例 2:

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

解法

我们用 dfs 遍历,从 JFK 开始,尝试所有可能的选择,这需要知道当前可以飞哪些城市,需要构建邻接表。根据当前选择,往下递归,尝试找出第一个用完机票的路径,如果找不出来,返回false,否则,返回true。

访问过的边要删掉。比如,北京飞广州,到了广州,北京的邻居list中删掉广州。

为什么要返回真假,因为要用它判断要不要提前回溯,在该分支走不下去,就要离开。

我们选择飞入的城市,如果困住了,就是飞错了,要回溯,将北京的邻居list中删除的广州恢复回来,不飞广州了,飞别的试试,离开当前分支,切入别的分支,继续探索路径。

代码

const findItinerary = (tickets) => {
  const res = ['JFK']; // 初始放入起点'JFK'
  const map = {};      // 邻接表

  for (const ticket of tickets) { // 遍历tickets,建表
    const [from, to] = ticket;    // 每一张机票,读出起点和终点
    if (!map[from]) {
      map[from] = []; // 初始化
    }
    map[from].push(to); // 建立映射
  }

  for (const city in map) { // 按照字母顺序,小的在前
    map[city].sort();
  }

  const dfs = (city, used) => { // city是当前访问的城市、used是已用掉的机票数
    if (used == tickets.length) { // 用光了所有机票,路径找到了
      return true;
    };
    const nextCities = map[city]; // 获取下一站能去的城市list
    if (!nextCities || nextCities.length == 0) { // 没有邻接城市了
      return false; // 还没用光机票,就没有下一站了,返回false
    }
    for (let i = 0; i < nextCities.length; i++) { // 设置出各种选项(递归分支)
      const next = nextCities[i]; // 当前选择的下一站
      nextCities.splice(i, 1);    // 飞出地的list中删掉这一站
      res.push(next);             // 将该选择推入res
      if (dfs(next, used + 1)) {  // 在该递归分支中能找到一个用完所有机票的路径
        return true;
      } else {
        nextCities.splice(i, 0, next); // 将删掉的这一站重新插回去
        res.pop();                     // 推入res的选择,也撤销
      }
    }
  };

  dfs('JFK', 0); // 从'JFK'城市开始遍历,当前用掉0张机票
  return res;
};