Leetcode # 632. Smallest Range Covering Elements from K Lists
- 2023.09.06
- ★★★ Hard heqpq Interval 區間 LeetCode Priority Queue Sliding Window
Problem
https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists
Solution: Sliding Window
[Time Limit Exceeded 86 / 88 testcases passed]
all_nums := list(itertools.chain.from_iterable(nums))
max_num := max(all_nums)
min_num := min(all_nums)
m := len(nums)
n := len(all_nums)
Time Complexity: O((max_num – min_num) * m)
Space Complexity: O(m)
(The input and output generally do not count towards the space complexity.)
class Solution: def smallestRange(self, nums: List[List[int]]) -> List[int]: left = min([nums_[ 0] for nums_ in nums]) end = max([nums_[-1] for nums_ in nums]) ans = [left, end] pointers = [0 for _ in nums] for right in range(left, end + 1): for i, nums_ in enumerate(nums): while pointers[i] < len(nums_) - 1 and nums_[pointers[i] + 1] <= right: pointers[i] += 1 if all(left <= nums_[pointers[i]] <= right for i, nums_ in enumerate(nums)): left = min([nums_[pointers[i]] for i, nums_ in enumerate(nums)]) if right - left < ans[1] - ans[0]: ans = [left, right] return ans
Solution: Using Pointers
[Time Limit Exceeded 86 / 88 testcases passed]
每個 nums[i] 分配一個 pointers[i]
pointers[i] 從 index 0 開始
每次從尚未到終點的 pointers[i] 中
選取其值 nums[i][pointers[i]] 最小者
將其向後移動 1
若有一 pointers[i] 遍歷至終點
則終止 while loop (參考 Solution: Priority Queue – 2.)
Time Complexity: O(n * m)
while loop 執行 n 次
每一次 while loop 中 執行 m 次的 for loop
Space Complexity: O(m)
(The input and output generally do not count towards the space complexity.)
class Solution: def smallestRange(self, nums: List[List[int]]) -> List[int]: first_elements = [nums_[0] for nums_ in nums] ans = [min(first_elements), max(first_elements)] pointers = [0] * len(nums) # [-1] + [0] * (len(nums) - 1) next_moved_i = 0 # while loop: n times # while next_moved_i >= 0: while pointers[next_moved_i] < len(nums[next_moved_i]): # pointers[next_moved_i] += 1 # this line is moved to bottom # find min_num and max_num, m times # min_can_be_moved, next_moved_i = inf, -1 min_num, max_num = inf, -inf for i, nums_ in enumerate(nums): # m times num = nums_[pointers[i]] # update min_num # min_num = min(min_num, num) if num < min_num: min_num, next_moved_i = num, i max_num = max(max_num, num) """ if pointers[i] < len(nums_) - 1 and num < min_can_be_moved: min_can_be_moved = num next_moved_i = i """ # update ans if max_num - min_num < ans[1] - ans[0]: ans = [min_num, max_num] pointers[next_moved_i] += 1 # moved from line 10 return ans
Solution: Priority Queue
Python 請參考 heapq
- pop 掉最小的
heap[0]
≡nums[i][j]
再將其之後的nums[i][j + 1]
push 進heap
替換它
(這和上一個 solution 的做法是一樣的
但是改用 priority queue 使時間複雜度從 O(m) 減少至 O(log(m)))
- 當
nums[i]
已被遍歷至底
就沒有繼續向後遍歷其他nums[k]
(k != i) 的必要
因為如此要包含nums[i][j]
及其他元素 numk ∈ nums[k] 的閉區間只可能越來越大 - 關於
max_num
:- 當 heap 中的最大值
nums[i][j]
=max_num
被 pop 出
但因為所有nums[i]
為 non-decreasing order
故用以替代的下一個nums[i][j + 1]
必定>= nums[i][j]=max_num
nums[i][j + 1]
會成為新的max_num
- 若無下一個 num,根據 2. 則結束 while loop
- 當 heap 中的最大值
Time Complexity: O(m + n * log(m)) = O(n * log(m))
Space Complexity: O(m)
(The input and output generally do not count towards the space complexity.)
class Solution: def smallestRange(self, nums: List[List[int]]) -> List[int]: m = len(nums) heap = [] max_num = -inf # O(m) for i in range(m): heappush(heap, (nums[i][0], i, 0)) max_num = max(max_num, nums[i][0]) # heap[0][0] ≡ min(heap)[0] ans = [heap[0][0], max_num] while True: # O(n) print([h[0] for h in heap]) # pop 掉最小的 heap[0] ≡ nums[i][j] _, i, j = heappop(heap) # O(log(m)) if (j := j + 1) == len(nums[i]): break next_num = nums[i][j] # 如果新的值比原最大值更大則替換 max_num = max(max_num, next_num) # 推入 nums[i][j + 1] 替代被 pop 掉的 nums[i][j] heappush(heap, (next_num, i, j)) # O(log(m)) if max_num - heap[0][0] < ans[1] - ans[0]: ans = [heap[0][0], max_num] return ans
Last Updated on 2023/09/06 by A1go