300. Longest Increasing Subsequence
题目解释:给出一个没有排序的整型数组,找到最长的升序序列的长度。
Example:
Input:
[10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
注意
- 可能会存在多个相同长度的最长升序序列,你只是需要返回他们的长度即可。
- 算法的时间复杂度必须为
Follow up:尝试优化你的算法,使其时间复杂度为
题目分析:首先,在分析题目的时候,我们不是求解最长升序子序列,而是序列,即不要求数字相邻。既然出现了最长这个字眼,我们可以尝试往动态规划这个方向去想。
设计一个数组dp,其中容易得到状态转移方程为dp[i]=max(dp[i],dp[j]+1),其中dp[i]的值为nums[:i]中升序数组的长度。
那么这样就是比较容易的写出了整个的解题代码:
def lengthOfLIS(self, nums):
"""
基于简单DP:时间复杂度为O(n^2),空间复杂度为O(n),这个也是计算最长升序序列的方法
:type nums: List[int]
:rtype: int
"""
# 初始化dp数组
dp=[1]*len(nums)
for i in range(len(nums)):
for j in range(i):
# 从0-i中看比nums[i]小的元素,一轮更新完成后dp[i]的值为nums[:i]中升序数组的长度
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp) if dp else 0
那么可以继续进行思考,怎么把算法进行优化,使其时间复杂度为呢?
可能一眼看过去是没有任何思路的。然后,我们去分析时间复杂度,既然时间复杂度是要求,出现了logn这个选项,那么可不可以尝试使用二分查找呢?因为外面还乘了一个n,一般情况下是一次遍历。
但是又出现了一个问题,就是我们在使用二分查找的时候,要求数组是已经排完序的,而我们的数组是没有排序的。
使用一个二分查找的方式,找到递增子数组中大于等于当前值的第一个数的位置;
如果找到,则利用当前值替换;否则将当前值加入到递增子数组中,表明该值比子数组的值都大,可能输入子数组。
例子:nums = [5,6,7,1,2,8,3,4,0,5,9]:
遍历到 7: res = [5,6,7];
遍历到 2: res = [1,2,7];
遍历到 8: res = [1,2,7,8];
遍历到 3: res = [1,2,3,8];
遍历到 4: res = [1,2,3,4];
剩下三个元素 : res = [0,2,3,4,5,9];
最后我们就可以得到最长递增子序列的长度,但是这里要注意得到的子序列不是真正的子序列,然而其长度是正确的。
该算法无法得到最长递增子序列,仅计算长度。
def lengthOfLIS(self, nums):
"""
二分查找,时间复杂度O(nlogn),空间复杂度O(n)
"""
tails = [0] * len(nums)
size = 0
for x in nums:
i, j = 0, size
while i != j:
m = (i + j) / 2
if tails[m] < x:
i = m + 1
else:
j = m
tails[i] = x
size = max(i + 1, size)
return size
Reference
https://blog.csdn.net/Koala_Tree/article/details/80062211
总结
2019/6/10:Good Good study,day day up.