携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情
每日刷题 2022.08.26
- leetcode原题链接:leetcode.cn/problems/ur…
- 难度:中等
- 方法:拓扑排序
题目
- 给定一个长度为 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;
};