1. 程式人生 > 其它 >LeetCode 354. 俄羅斯套娃信封問題(Python、動態規劃LIS、貪心+二分查詢)

LeetCode 354. 俄羅斯套娃信封問題(Python、動態規劃LIS、貪心+二分查詢)

技術標籤: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。另外,我們使用的排序演算法也可能需要額外的空間。