[拓扑排序] 剑指 Offer II 115. 重建序列

130 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情

每日刷题 2022.08.26

题目

  • 给定一个长度为 n 的整数数组 nums ,其中 nums 是范围为 [1,n] 的整数的排列。还提供了一个 2D 整数数组 sequences ,其中 sequences[i] 是 nums 的子序列。
  • 检查 nums 是否是唯一的最短 超序列 。最短 超序列 是 长度最短 的序列,并且所有列 sequences[i] 都是它的子序列。对于给定的数组 sequences ,可能存在多个有效的 超序列 。
    • 例如,对于 sequences = [[1,2],[1,3]] ,有两个最短的 超序列 ,[1,2,3] 和 [1,3,2] 。
    • 而对于 sequences = [[1,2],[1,3],[1,2,3]] ,唯一可能的最短 超序列 是 [1,2,3] 。[1,2,3,4] 是可能的超序列,但不是最短的。
  • 如果 nums 是序列的唯一最短 超序列 ,则返回 true ,否则返回 false 。
  • 子序列 是一个可以通过从另一个序列中删除一些元素或不删除任何元素,而不改变其余元素的顺序的序列。

示例

  • 示例1
输入:nums = [1,2,3], sequences = [[1,2],[1,3]]
输出:false
解释:有两种可能的超序列:[1,2,3][1,3,2]。
序列 [1,2][1,2,3][1,3,2]的子序列。
序列 [1,3][1,2,3][1,3,2]的子序列。
因为 nums 不是唯一最短的超序列,所以返回false。
  • 示例2
输入:nums = [1,2,3], sequences = [[1,2]]
输出:false
解释:最短可能的超序列为 [1,2]。
序列 [1,2] 是它的子序列:[1,2]。
因为 nums 不是最短的超序列,所以返回false
  • 示例3
输入:nums = [1,2,3], sequences = [[1,2],[1,3],[2,3]]
输出:true
解释:最短可能的超序列为[1,2,3]。
序列 [1,2] 是它的一个子序列:[1,2,3]。
序列 [1,3] 是它的一个子序列:[1,2,3]。
序列 [2,3] 是它的一个子序列:[1,2,3]。
因为 nums 是唯一最短的超序列,所以返回true。

提示

  • n == nums.length
  • 1 <= n <= 10^4
  • nums 是 [1, n] 范围内所有整数的排列
  • 1 <= sequences.length <= 10^4
  • 1 <= sequences[i].length <= 10^4
  • 1 <= sum(sequences[i].length) <= 10^5
  • 1 <= sequences[i][j] <= n
  • sequences 的所有数组都是 唯一 的
  • sequences[i] 是 nums 的一个子序列

解题思路

  • 根据题意:根据sequences中所给的数组,判断nums是否是最短的超序列。
  • 分析:
    • sequences中的所有的数组都是nums的子序列,因此不存在形成环的情况,可以使用拓扑排序将所有的节点全部依次打印出来。
    • nums是所有整数的排列,因此只存在一种顺序。那么当拓扑排序中某一次存在多个入度为0的节点的时候,就表示可以由sequences中数组中形成的超序列不止一个,那么nums就不是唯一
    • 所求最短的超序列,也就是刚刚好将sequences数组中的所有的整数都利用起来,形成的拓扑序列,因此最终将拓扑序列出来的结果长度和nums长度比较,相等的话,就是最短

新的技巧

  • 存储图的时候,一般是使用二维数组,可以使用不定长数组,当所给的样例中存在重复的边的时候。那么二维数组中就会存在一个节点存储多遍其相连的节点,而对于数组而言,查找其中是否存在该节点,最坏的时间复杂度为o(n),所以想了优化的方法,就是二维数组内部的不定长数组,将其转换成set集合,那么每次添加边的时候,就会自动去重。
  • 使用set集合还会存在问题,因为你只是将边与边之间的重复映射关系去重了,但是每次edgs数组记录每个节点的入度的时候,就会存在问题。因此当使用set集合的时候,就需要判断当前的节点set集合中是否存在,不存在的时候给当前节点的入度++,否则不对当前节点的入度做操作。

AC代码

var sequenceReconstruction = function(nums, sequences) {
  // 拓扑排序貌似可以解决问题
  // 1.建图并且创建入度数组
  const n = nums.length;
  let grap = new Array(n + 1).fill(0).map(() => new Array()), edge = new Array(n + 1).fill(0);
  const ss = sequences.length , s = sequences;
  for(let i = 0; i < ss; i++) {
    let l = s[i].length;
    for(let j = 0; j < l - 1; j++) {
      let pre = s[i][j], nxt = s[i][j + 1];
      // 需要去重
      if(grap[pre].indexOf(nxt) === -1) {
        grap[pre].push(nxt);
        edge[nxt]++;
      }
    }
  }
  // 2.遍历每一个节点,如果有多个入度为0,则false
  let queue = [];
  for(let i = 1; i <= n; i++) {
    if(edge[i] == 0) queue.push(i);
  }
  if(queue.length > 1) return false;
  let ans = [];
  while(queue.length > 0) {
    if(queue.length > 1) return false;
    let cur = queue.pop(), len = grap[cur].length, node = grap[cur];
    ans.push(cur);
    while(len > 0) {
      let nx = node[len - 1];
      // console.log('nx:::', nx, node)
      edge[nx]--;
      if(edge[nx] === 0) queue.push(nx);
      len--;
    }
  }
  // 3.得到拓扑序列,将其与Nums比较,是否相同
  return nums.join('') === ans.join('') ? true : false; 
};