你有 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
-10^5 <= 元素的值 <= 10^5
对于使用Java的用户,请注意传入类型已修改为List>。重置代码模板后可以看到这项改动。
暴力版:
每次的区间,覆盖所有整数列表指向当前位置的值,然后每次将最小的那个位置向前移动。
拿用例来说:
第一次是[4, 0, 5]
,则区间为[0, 4]
,将最小的0
对应的位置向后走,
第二次是[4, 9, 5]
,则区间为[4, 9]
,将最小的4
对应的位置向后走,
第三次是[10, 9, 5]
,则区间为[5, 10]
,将最小的5
对应的位置向后走……
以此类推,直到某个列表走到头为止
设整数列表中,最短的列表长度为l
,则时间复杂度是l * k
这种方法超时了
超时的原因有2个,1个是while
循环里用的all
,每次循环都会对k
个列表进行判断,另外1个是求区间左边界和右边界的时候需要用max, min
,时间复杂度也比较高
改进:
while
循环中的all
:本意是用来检查列表是否走到头,所以可以考虑将每个列表的下标放在数据结构里,在循环内部判断是否到达该列表的最后一个值,while
循环改用while True
超时普通版:
class Solution:
def smallestRange(self, nums: List[List[int]]) -> List[int]:
n = len(nums)
index_list = [0] * n
ans = [-10 ** 5, 10 ** 5]
while all(index_list[i] < len(nums[i]) for i in range(n)):
interval_left, min_index = min((nums[i][index_list[i]], i) for i in range(n))
interval_right = max(nums[i][index_list[i]] for i in range(n))
ans = min(ans, [interval_left, interval_right], key = lambda x: x[1] - x[0])
index_list[min_index] += 1
return ans
改进版:
class Solution:
def smallestRange(self, nums: List[List[int]]) -> List[int]:
n = len(nums)
ans = [-10 ** 5, 10 ** 5]
min_heap = []
interval_right = 0
for i in range(n):
heapq.heappush(min_heap, (nums[i][0], i, 0))
interval_right = max(interval_right, nums[i][0])
while True:
interval_left, num_index, index = heapq.heappop(min_heap)
ans = min(ans, [interval_left, interval_right], key = lambda x: x[1] - x[0])
if index == len(nums[num_index]) - 1:
break
interval_right = max(interval_right, nums[num_index][index + 1])
heapq.heappush(min_heap, (nums[num_index][index + 1], num_index, index + 1))
return ans