一天一大 lee(重新安排行程)难度:中等-Day20200827

时间:2022-07-25
本文章向大家介绍一天一大 lee(重新安排行程)难度:中等-Day20200827,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目:[1]

给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。

所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。

说明

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

示例

  1. 示例 1
输入: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
输出: ["JFK", "MUC", "LHR", "SFO", "SJC"]
  1. 示例 2
输入: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
输出: ["JFK","ATL","JFK","SFO","ATL","SFO"]
解释: 另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"]。但是它自然排序更大更靠后。

抛砖引玉

抛砖引玉

思路

  • 数组 tickets,索引:0->起点,1->终点
  • 遍历数组 tickets,如果两个子集起点终点相等则说明两个子集连续,可以拼接起来
  • 要求每个 tickets 的子集都需要有节点被拼接到结果数组中
  • 如果同一个终点对应多个起点按照起点字母排序拼接

实现

  • 遍历 tickets 生成 map,起点作为 map 的哈希:此时涉及重复起点的问题,题目要求按照字符大小排序,则 map 的 value 存放:到达的集合
  • 从指定起点开始一直查询起点对应的终点,直到无对应终点结束

在枚举路线时,开始的思路是根据起点查到其对应的终点,从前向后遍历这些可能的终点,遇到终点不能作为其他子集起点则跳过最后拼接。

这种思路实现后会发现有些情况会使 tickets 的子集部分不被拼接(及 map 的 value 不能被清空):

  • 遍历 map 的 value 时存在元素不连接其他起点
  • 一个终点对应多个起点时选中当前排序小的起点,时也存在后续节点不被连接的情况

枚举路线的逻辑:

  1. 递归到 map 的 value 为空时开始反向(从后向前填充)
  2. 枚举多个起点递归,只保留可以递归到 map 的 value 为空的情况

递归

终点查询的逻辑借助递归完成

  • 参数:起点 start
  • 终止条件:map 中无对应起点
  1. 递归到 map 的 value 为空时开始反向(从后向前填充)
/**
 * @param {string[][]} tickets
 * @return {string[]}
 */
var findItinerary = function (tickets) {
  let _result = [],
    len = tickets.length,
    map = new Map()

  for (let i = 0; i < len; i++) {
    if (map.has(tickets[i][0])) {
      let items = map.get(tickets[i][0])
      items.push(tickets[i][1])
      items.sort()
      map.set(tickets[i][0], items)
    } else {
      map.set(tickets[i][0], [tickets[i][1]])
    }
  }

  function getEnd(start) {
    let items = map.get(start)
    // 枚举 选择传入节点对应的终点
    while (items && items.length) {
      getEnd(items.shift())
    }
    // 优先从map-value遍历完成的节点存放
    _result.unshift(start)
  }
  getEnd('JFK')
  return _result
}
  1. 枚举多个起点递归,只保留可以递归到 map 的 value 为空的情况
/**
 * @param {string[][]} tickets
 * @return {string[]}
 */
var findItinerary = function (tickets) {
  let _result = ['JFK'],
    len = tickets.length,
    map = new Map()

  for (let i = 0; i < len; i++) {
    if (map.has(tickets[i][0])) {
      let items = map.get(tickets[i][0])
      items.push(tickets[i][1])
      items.sort()
      map.set(tickets[i][0], items)
    } else {
      map.set(tickets[i][0], [tickets[i][1]])
    }
  }

  function getEnd(start, step) {
    // 满足能每个子元素都拼接到
    if (step === len) return true

    let items = map.get(start) || []

    // 不存在与当前起点连接的终点
    if (items.length === 0) return false

    for (let i = 0; i < items.length; i++) {
      let item = items[i]

      // 一个起点对应多个终点时,如果选择当前起点
      items.splice(i, 1)
      _result.push(item)

      // 检查是否能 - 满足能每个子元素都拼接到
      if (getEnd(item, step + 1)) {
        return true
      } else {
        // 如果不满足此时还不能选择这个点与当前起点连接
        items.splice(i, 0, item)
        _result.pop()
      }
    }
  }

  getEnd('JFK', 0)

  return _result
}

[1]

题目:: https://leetcode-cn.com/problems/reconstruct-itinerary//