给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
dp[i]
表示以nums[i]
结尾的最长上升子序列长度,更新规则为:
d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) , if n u m s [ i ] > n u m s [ j ] , ∀ j < i dp[i] = max(dp[i], dp[j]+1), \;\; \text{if}\;\; nums[i]>nums[j], \forall j < i dp[i]=max(dp[i],dp[j]+1),ifnums[i]>nums[j],∀j<i
初始时dp[0]=1
时间复杂度: o ( n 2 ) o(n^2) o(n2)
空间复杂度: o ( n ) o(n) o(n)
参考:https://leetcode.com/problems/longest-increasing-subsequence/solutions/1326308/c-python-dp-binary-search-bit-segment-tree-solutions-picture-explain-o-nlogn/?orderBy=most_votes
看了好几篇题解都没看懂,终于找到一篇写得很清楚的了……
以nums = [2, 6, 8, 3, 4, 5, 1]
为例,来找出所有上升子序列
sub1 = [2]
sub1 = [2,6]
sub1 = [2,6,8]
3
,不能往sub1
里加了,但是想留下来3
,因为越小的数字,对我们找到长的上升子序列越有利,此时我们再开一个数组保存当前结果,也即此时有:sub1 = [2,6,8], sub2 = [2,3]
4
,不能往sub1
里加,但是可以往sub2
里加,此时有:sub1=[2,6,8], sub2=[2,3,4]
5
,同理加到sub2
里,此时有:sub1=[2,6,8], sub2=[2,3,4,5]
1
,不能加到sub1, sub2
里,因此再开一个,此时有:sub1=[2,6,8], sub2=[2,3,4,5], sub3=[1]
sub
中选择最长的那个,res = len(sub2) = 4
思路是这么个思路,但是发现要消耗比较多空间来保存sub
数组,不太划算,所以考虑合并这些数组。当碰到新的数字时,可以直接替换已有的数组中的数字。如上面第4步,可以直接把3
插入到sub1
里,替换掉6
。这种方法不会影响sub1
的长度,因此假设最终sub1
仍然为最长数组,那么长度没有变化,len(sub1)
仍然是正确答案。同时,这种方法可以保留新的小一些的数字,为之后更新数组做贡献,比如按此方法替换掉6
后,在第5步时,又会用4
替换掉8
,此时sub1
完全被sub2
取代
替换数字时,因为sub
数组是递增的,所以可以用二分查找,找到当前数字能够插入的最左索引即可
时间复杂度: o ( n log n ) o(n\log n) o(nlogn)
空间复杂度: o ( n ) o(n) o(n)
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
dp = [1] * len(nums)
for i in range(1, len(nums)):
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
aux_stack = [nums[0]]
for each_num in nums[1:]:
if each_num > aux_stack[-1]:
aux_stack.append(each_num)
else:
index = bisect.bisect_left(aux_stack, each_num)
aux_stack[index] = each_num
return len(aux_stack)