題目描述:
給定一個無序的整數數組,找到其中最長上升子序列的長度。
示例:
輸入: [10,9,2,5,3,7,101,18]
輸出: 4
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。
說明:
可能會有多種最長上升子序列的組合,你只需要輸出對應的長度即可。
你算法的時間複雜度應該爲 O(n2) 。
進階: 你能將算法的時間複雜度降低到 O(n log n) 嗎?
思考:
子序列和子串是不同的,子串一定的連續的,子序列不一定是連續的。
方法一
思路1:動態規劃(時間複雜度O())
代碼實現:
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())
最長遞增子序列與patience game的紙牌遊戲有關,有一種排序方法叫耐心排序。思想如下:
一排撲克牌,遍歷時根據一定規則把撲克牌分成若干堆。規則是:
- 只能把點數小的排壓到點數大的牌上。(牌數一樣大也可以)
- 如果大的牌沒有可放置的堆,就開闢新的堆。
- 如果一張牌有多個可選擇放置的堆,就放在最左面的堆上(這樣可以保證堆頂有序)。
例如:(假如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