题目:
你有 k 个升序排列的整数数组。找到一个最小区间,使得 k 个列表中的每个列表至少有一个数包含在其中。
我们定义如果 b-a < d-c 或者在 b-a == d-c 时 a < c,则区间 [a,b] 比 [c,d] 小。
示例:
输入:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
输出: [20,24]
解释:
列表 1:[4, 10, 15, 24, 26],24 在区间 [20,24] 中。
列表 2:[0, 9, 12, 20],20 在区间 [20,24] 中。
列表 3:[5, 18, 22, 30],22 在区间 [20,24] 中。
注意:
- 给定的列表可能包含重复元素,所以在这里升序表示 >= 。
- 1 <= k <= 3500
- -105 <= 元素的值 <= 105
抛砖引玉
思路
刚看本题确实没有什么思路,看了官方的题解先去完成了最小覆盖子串:
more-011: 最小覆盖子串 (难度:困难)
在回看思路就比较明显了:
- 把nums矩阵合并成一个有序数组,且记录每一个元素来源的行数
- 最终求覆盖所有行数的数字区间
逻辑
- 即要记录value又要记录行数,数组(二维){index:行数,value:值}
- 新排序数组dp
- left,right新数组指针
- map记录待匹配的行索引即每行数量
- Rleft,Rright终止区间
/**
* @param {number[][]} nums
* @return {number[]}
*/
var smallestRange = function(nums) {
let dp = [],
map = new Map(),
left = 0,
right = 0,
Rleft = -Number.MAX_VALUE, // 结果区间的left索引
Rright = -1, // 结果区间的right索引
type = 0; // 区间中数字来源的行数种类
// 生成二维映射数组dp 行映射map
for (let i = 0; i < nums.length; i++) {
map.set(i, 0);
for (let j = 0; j < nums[i].length; j++) {
dp.push({ index: i, value: nums[i][j] })
}
}
// dp排序
dp.sort((a, b) => a.value - b.value);
// 右边界
while (right < dp.length) {
let Rindex = dp[right].index, // 指针所在的数字来源行
Rvalue = dp[right].value, // 指针所在的数字
RMvalue = map.get(Rindex) // 区间内改行数字数量
// 区间新增 来源于新行 则行类型+1
if (RMvalue === 0) ++type
// 记录对应新增数字个数
map.set(Rindex, ++RMvalue)
// 来源行包含所有行即等于map-size
while (type === map.size && left <= right) {
let Lindex = dp[left].index,
Lvalue = dp[left].value,
LMvalue = map.get(Lindex);
// 如果新的区间比较小则使用新区间
if (Rvalue - Lvalue < Rright - Rleft) {
Rleft = Lvalue
Rright = Rvalue
}
// 丢弃一个数字,map记录减一
map.set(Lindex, --LMvalue)
// 丢失数字的计算为0,则说明该来源行在区间中无数字,减少行计数
if (LMvalue === 0) --type
// 做区间向右移动
left++
}
// 右区间向右移动
right++
}
return [Rleft, Rright]
};
博客: 小书童博客
每天的每日一题,写的题解会同步更新到公众号一天一大 lee 栏目
欢迎关注留言
公号: 坑人的小书童