题目地址:
https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/
给定 k k k个非空已排序列表 A A A,要求返回一个最短的区间 [ a , b ] [a,b] [a,b],使得每个列表至少有一个数含在 [ a , b ] [a,b] [a,b]里。如果有多个区间满足要求,则返回左端点最小的那个。
思路是多路归并。开个最小堆,里面存长度是 3 3 3的数组 c c c,其中 c [ 1 ] c[1] c[1]代表列表下标, c [ 2 ] c[2] c[2]代表数字在 A [ c [ 1 ] ] A[c[1]] A[c[1]]里的下标, c [ 0 ] = A [ c [ 1 ] ] [ c [ 2 ] ] c[0]=A[c[1]][c[2]] c[0]=A[c[1]][c[2]]。先将每个列表的第一个数字连同其下标加入这个最小堆,同时开个变量 M M M存一下堆里的最大值,接着开始多路归并。每次从堆中出一个数组 c c c,那么包含 c [ 0 ] c[0] c[0]的且包含所有其他列表里至少一个数的区间就应该是 [ c [ 0 ] , M ] [c[0],M] [c[0],M],以此区间更新答案(就是说如果这个区间更短,那就把答案设为这个区间。由于我们就是按左端点由小到大枚举的,所以不需要考虑左端点的问题)。更新完答案之后,再将 A [ c [ 1 ] ] [ c [ 2 ] + 1 ] A[c[1]][c[2]+1] A[c[1]][c[2]+1]这个数及其下标入堆,然后重复上面的操作。当 A [ c [ 1 ] ] [ c [ 2 ] + 1 ] A[c[1]][c[2]+1] A[c[1]][c[2]+1]这个数不存在的时候(即 A [ c [ 1 ] ] A[c[1]] A[c[1]]这个列表已经枚举完了),此时左端点为 A [ c [ 1 ] ] [ c [ 2 ] ] A[c[1]][c[2]] A[c[1]][c[2]]的区间就已经枚举完了,后面再枚举就不会得到含 A [ c [ 1 ] ] [ c [ 2 ] ] A[c[1]][c[2]] A[c[1]][c[2]]这个数的区间了,就不满足条件了,所以可以直接结束枚举,退出循环(这里有一些不严谨,因为如果后面枚举了只含 A [ c [ 1 ] ] [ c [ 2 ] ] A[c[1]][c[2]] A[c[1]][c[2]]的单点区间的话,这个区间仍然可能作为答案。但是这个单点区间在 A [ c [ 1 ] ] [ c [ 2 ] ] A[c[1]][c[2]] A[c[1]][c[2]]这个数出堆的时候已经枚举过了,所以并不影响算法的正确性)。代码如下:
import java.util.List;
import java.util.PriorityQueue;
public class Solution {
public int[] smallestRange(List<List<Integer>> nums) {
PriorityQueue<int[]> maxHeap = new PriorityQueue<>((p1, p2) -> Integer.compare(p1[0], p2[0]));
int max = Integer.MIN_VALUE;
for (int i = 0; i < nums.size(); i++) {
int val = nums.get(i).get(0);
maxHeap.offer(new int[]{val, i, 0});
max = Math.max(max, val);
}
int[] res = null;
while (true) {
int[] top = maxHeap.poll();
int l = top[0], r = max;
// 发现了更短的区间,则更新
if (res == null || res[1] - res[0] > r - l) {
res = new int[]{l, r};
}
int row = top[1], idx = top[2];
if (idx + 1 < nums.get(row).size()) {
int val = nums.get(row).get(idx + 1);
maxHeap.offer(new int[]{val, row, idx + 1});
max = Math.max(max, val);
} else {
// 某一行作为左端点枚举完毕,则退出循环
break;
}
}
return res;
}
}
时间复杂度 O ( n log k ) O(n\log k) O(nlogk), n n n是数字总个数,空间 O ( k ) O(k) O(k)。