题目描述:
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例2:
输入:nums = [0,1,0,3,2,3]
输出:4
示例3:
输入:nums = [7,7,7,7,7,7,7]
输出:1
思路1:
使用递归暴力求解,每个元素选或者不选,时间复杂度为O(2^n)。
思路2:
动态规划——dp[i] : 从头到元素i最长子序列的长度,同时保证第i个元素被选上;
所以结果就是max{dp[0], dp[1], dp[2]…}
状态转移方程:
dp[i] = max{dp[j]} + 1,其中 j 是从0~i-1
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
dp = [1 for i in range(len(nums))]
for l in range(1, len(nums)):
for j in range(0, l):
if nums[l] > nums[j]:
dp[l] = max(dp[l], dp[j] + 1)
return max(dp)
时间复杂度为O(n^2)。
思路3:
维护一个数组 lis,遍历数组nums,如果nums[i]比lis最大的大,加入到lis后端,否则用二分法找到在数组lis中比nums[i]大的最小的元素替换掉。
主要的思路就是,插入的值越小给后面留的希望越大。
示例:nums = [10,9,2,5,3,7,101,18, 20]
[10]
[9]
[2]
[2, 5]
[2, 3]
[2, 3, 7]
[2, 3, 7, 101]
[2, 3, 7 , 18]
[2, ,3, 7, 18, 20]
这样利用二分法之后,时间复杂度会变为O(nlogn)。
下面分别是利用库和不利用库的两个代码:
用库:
from sortedcontainers import SortedList
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) == 1:
return 1
res = SortedList()
res.add(nums[0])
for i in range(1, len(nums)):
if nums[i] > res[-1]:
res.add(nums[i])
else:
index = res.bisect_left(nums[i])
del res[index] # 注意这里不能直接使用赋值操作
res.add(nums[i])
return len(res)
不用库:
注意二分时确定pos
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) == 1:
return 1
res = []
res.append(nums[0])
for i in range(1, len(nums)):
if nums[i] > res[-1]:
res.append(nums[i])
else: # 进行二分查找 找比nums[i]大的最小值替换掉
pos, l, r = 0, 0, len(res)-1
print("nums[i]:", nums[i])
while l <= r:
mid = (l+r) // 2
if res[mid] == nums[i]:
pos = mid
break
elif res[mid] < nums[i]:
l = mid + 1
pos = l
elif res[mid] > nums[i]:
r = mid - 1
print("pos:", pos)
res[pos] = nums[i]
print(res)
return len(res)