LeetCode 354. 俄羅斯套娃信封問題(Python、動態規劃LIS、貪心+二分查詢)
阿新 • • 發佈:2020-12-28
技術標籤:LeetCode 動態規劃leetcodepython動態規劃快速排序演算法
學習這一演算法的思想,解決二維LIS問題,位元組跳動原題,值得反覆學習
該問題為最長遞增子序列的二維問題。
我們要找到最長的序列,且滿足 seq[i+1] 中的元素大於 seq[i] 中的元素。
該問題是輸入是按任意順序排列的——我們不能直接套用標準的 LIS 演算法,需要先對資料進行排序。我們如何對資料進行排序,以便我們的 LIS 演算法總能找到最佳答案?
我們可以在這裡找到最長遞增子序列的解決方法。如果您不熟悉該演算法,請先理解該演算法,因為它是解決此問題的前提條件。
演算法:
假設我們知道了信封套娃順序,那麼從裡向外的順序必須是按 w
在對信封按 w
進行排序以後,我們可以找到 h
上最長遞增子序列的長度。、
我們考慮輸入 [[1,3],[1,4],[1,5],[2,3]],如果我們直接對 h
進行 LIS 演算法,我們將會得到 [3,4,5],顯然這不是我們想要的答案,因為 w
相同的信封是不能夠套娃的。
為了解決這個問題。我們可以按 w
進行升序排序,若 w
相同則按 h
降序排序。(我的理解是:若 h
按降序排,那麼就可以過濾掉 w
相同的情況) 則上述輸入排序後為 [[1,5],[1,4],[1,3],[2,3]],再對 h 進行 LIS 演算法可以得到 [5],長度為 1,是正確的答案。這個例子可能不明顯。
我們將輸入改為 [[1,5],[1,4],[1,2],[2,3]]。則提取 h 為 [5,4,2,3]。我們對 h 進行 LIS 演算法將得到 [2,3],是正確的答案。
注意二分查詢中的程式碼註釋
class Solution:
def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
self.quickSort(envelopes, 0, len(envelopes) - 1) # 快排, envelopes.sort(key=lambda x: (x[0], -x[1]))
# 貪心演算法求h的LIS
d = []
for i in range(len(envelopes)):
if not d or envelopes[i][1] > d[-1]:
d.append(envelopes[i][1])
else:
loc = self.bisect_right(d, envelopes[i][1])
d[loc] = envelopes[i][1]
return len(d)
def quickSort(self, nums: '二維列表', left, right):
if left < right:
partition_index = self.partition(nums, left, right)
self.quickSort(nums, left, partition_index - 1)
self.quickSort(nums, partition_index + 1, right)
def partition(self, nums, left, right):
pivot = left
index = pivot + 1
for i in range(index, right+1):
# 按第一個元素升序,第二個元素降序排
if (nums[i][0] < nums[pivot][0]) or (nums[i][0] == nums[pivot][0] and nums[i][1] > nums[pivot][1]):
self.swap(nums, i, index)
index += 1
self.swap(nums, index-1, pivot)
return index - 1
def swap(self, nums, i, j):
nums[i], nums[j] = nums[j], nums[i]
def bisect_right(self, arr: '一維陣列', k: '要插入的數字'):
left = 0
right = len(arr) - 1
index = right
while left <= right: # 注意寫二分查詢時要有=
mid = (left + right) // 2
if k <= arr[mid]:
right = mid - 1
index = mid # =mid而不是mid-1
else:
left = mid + 1
return index
複雜度分析
- 時間複雜度: O ( N log N ) O(N \log N) O(NlogN)。其中 N N N 是輸入陣列的長度。排序和 LIS 演算法都是 O ( N log N ) O(N \log N) O(NlogN)。
- 空間複雜度: O ( N ) O(N) O(N),需要一個數組 d d d,它的大小可達 N N N。另外,我們使用的排序演算法也可能需要額外的空間。