300. 最长上升子序列
Q:
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
A:
1.常规dp
#
# @lc app=leetcode.cn id=300 lang=python3
#
# [300] 最长上升子序列
#
class Solution:
def lengthOfLIS(self, nums) -> int:
l=len(nums)
if l==0 or l==1:
return l
dp=[1 for i in range(l)]
#dp[i]表示截止到i的最长上升子序列长度(包括i本身)
for limit in range(l):
i,_max=limit-1,0
while i>=0:
_max=max(dp[i],_max) if nums[i]<nums[limit] else _max
i-=1
dp[limit]=_max+1
# print(dp)
return max(dp)
# print(Solution().lengthOfLIS([10,9,2,5,3,7,101,18]))
2.dp+二分
写不出来,看评论区大佬的,思路是既然要nlogn,必然要二分,二分就必然要有序序列才能二分,所以dp数组存目前为止的最长上升序列,对于nums的当前考察元素nums[i],在dp上升序列里找比其大的最小值,替换为nums[i],当然如果没有比nums[i]大的就直接添加到上升序列末尾。
关键点是向中间插入的情况:如已存的上升序列为2,3,6,8,这时候nums[i]为5,按上面思路应当是覆盖掉6,即变为2,3,5,8。这样覆盖之后并没有改动上升序列的长度,还是4。这样覆盖的原因是5比6更小,理论上可以和更多的数字形成上升序列(如后面又有个6),那么就把6再把8覆盖。不然6和6是构不成上升序列的,反正确实有点不好理解,属于比较巧妙的非常规思路。
class Solution:
def lengthOfLIS(self, nums) -> int:
l=len(nums)
if l==0 or l==1:
return l
res=0
tails=[0 for i in range(l)]
#tails[i]记录截止到i的最长上升子序列末尾元素值
for num in nums:
le,ri=0,res
while le<ri:
mi=le+(ri-le)//2
if tails[mi]<num:
le=mi+1
else:
ri=mi
tails[le]=num
if le==res:
res+=1
print(tails,res)
return res
进击的小🐴农