300. 最長上升子序列

題目描述:
給定一個無序的整數數組,找到其中最長上升子序列的長度。
示例:

輸入: [10,9,2,5,3,7,101,18]
輸出: 4 
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。

說明:

可能會有多種最長上升子序列的組合,你只需要輸出對應的長度即可。
你算法的時間複雜度應該爲 O(n2) 。
進階: 你能將算法的時間複雜度降低到 O(n log n) 嗎?

思考:
子序列和子串是不同的,子串一定的連續的,子序列不一定是連續的。

方法一

思路1:動態規劃(時間複雜度O(N2N^{2}))
代碼實現:

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        length = len(nums)
        if length == 0:
            return 0
        dp = [1] * length    # 先都賦值爲1,因爲最小子序列長度爲1
        for i in range(length):
            for j in range(i):
                if nums[j] < nums[i]:
                    dp[i] = max(dp[i],dp[j] + 1)
        return max(dp)       # 返回dp中的最大值,即最大子序列的長度

方法二

思路2:二分查找(時間複雜度O(NlogNNlogN))
最長遞增子序列與patience game的紙牌遊戲有關,有一種排序方法叫耐心排序。思想如下:
一排撲克牌,遍歷時根據一定規則把撲克牌分成若干堆。規則是:

  1. 只能把點數小的排壓到點數大的牌上。(牌數一樣大也可以)
  2. 如果大的牌沒有可放置的堆,就開闢新的堆。
  3. 如果一張牌有多個可選擇放置的堆,就放在最左面的堆上(這樣可以保證堆頂有序)。

例如:(假如A最大)
在這裏插入圖片描述

牌的堆數就是我們想求的最長遞增子序列的長度。
在這裏插入圖片描述
所以該問題就轉換爲找合適的位置放置紙牌,因爲堆頂有序(從小到大),所以可以用二分查找。
代碼實現:

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        length = len(nums)
        if length == 0:
            return 0
        top = [0]*length       # 堆頂元素,即每個堆的最小值,因爲只有小牌只能放到打牌上面
        sumnum = 0             # 堆的個數
        for poker in nums:
            left = 0           # 第一個堆
            right = sumnum     # 最後一個堆
            while left < right:
                mid = (left + right) // 2
                if poker <= top[mid]:   # 如果牌數小於等於中間堆牌數,則當前中間堆可以放置該牌,但是因爲規則是放在最左堆,所以需要去左半部分繼續尋找
                    right = mid
                else:                   # 如果牌數大於中間堆,則當前中間堆不能放置該牌,需要去右半部分繼續尋找
                    left = mid + 1
            if left == sumnum:  # 沒找到合適的位置放置
                sumnum += 1     # 新建一個堆
            top[left] = poker   # 更新每個堆的最小值
        return sumnum
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章