题目描述
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
算法思路
DP
动态规划。
1、子序列:不要求连续,只要保证元素前后顺序一致即可;
2、上升:这里的“上升”是“严格上升”,元素不能相等;
状态转移方程:
遍历到索引是 i
的数的时候,我们应该把索引是 [0, ... ,i - 1]
的 DP数组(序列长度)都看一遍,如果当前的数 nums[i]
大于之前的某个数,那么 nums[i]
就可以接在这个数后面形成一个更长的序列 。把前面的 i 个数都看了, i对应的最长序列即比当前数nums[i]
要小的那些序列长里,找最大的,然后加 1 。
算法:
n=len(nums)
if n<2:return n
dp=[1]*n
for i in range(1,n):
for j in range(i):
if nums[i]>nums[j]:
dp[i]=max(dp[i],dp[j]+1)
return max(dp)
双重遍历,时间复杂度为
DP+二分查找
妈耶,差点就看不懂答案了,哭。
这里创建维护一个DP数组,定义为:下标i表示序列长为i+1的序列,dp[i]表示这个长度的序列的最小尾数。
遍历数组nums。
如果当前值K-2大于所有的dp值,那么表示当前的最大序列i可以+1,最新的序列长度为i+1,尾数就是当前值K-2;
再然后出现一个值K,大于所有的dp值,那么表示最大序列长度为i+2,尾数就是当前值K;
再然后出现一个值为K+1,也大于所有dp值,那么有最新序列长度为i+3,尾数为K+1;
下一个数是K-1,k-2<k-1<k
,所以这个数可以接在尾数为K-2的序列后面,即更新长度为i+2的序列的尾数。
class Solution(object):
def lengthOfLIS(self, nums):
tails, res = [0] * len(nums), 0
for num in nums:
i, j = 0, res
while i < j:
m = (i + j) // 2
if tails[m] < num: i = m + 1
# 如果要求非严格递增,将此行 '<' 改为 '<=' 即可。
else: j = m
tails[i] = num
if j == res: res += 1
return res
执行用时 :28 ms, 在所有 Python 提交中击败93.27%的用户
内存消耗 :12.8 MB, 在所有 Python 提交中击败了5.45%的用户
比第一种方法快了30倍。
时间复杂度为一层遍历