leetcode 300 Longest Increasing Subsequence 最长上升子序列
leetcode 2020年3月 每日一题打卡
据说华为问过这道题
问题:
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明: 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
原题链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
知识: 动态规划
适合使用动规求解的问题:
- 问题具有最优子结构。
- 无后效性,多指求最优解问题。
动规解题思路:
- 划分子问题
- 确定状态
- 确定状态转移方程
- 确定起始条件或边界条件
递归变动规,就是将含有n个参数的递归函数,变为一个n维数组,存储的值等于递归函数的返回值。
思路: python 动态规划,child[i] 表示以nums里第i个元素结尾的序列里最长上升子序列的长度。子问题:求解前i个元素里最长上升子序列的长度。状态:child[i]。状态转移方程:每个子问题中,遍历所有先前元素nums[j],如果nums[j]<nums[i],则child[i]等于所有child[j]+1中的最大值。边界状态:起始值为1。时间复杂度:O(n^2) , 动态规划的状态数为 n,计算状态时,需要 O(n) 的时间遍历之前的状态,所以总时间复杂度为 O(n^2)。空间复杂度:O(n),因为需要额外使用长度为 n的数组。
细节:
- 排除nums为空的情况
- best_i起始值设为1而不能为0
代码:
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
# 动态规划 O(N^2)
child=[] # child[i]表示以nums里第i个元素结尾的序列里最长上升子序列的长度
if nums == []:
return 0
child.append(1)
for i in range(1,len(nums)):
best_i=1
for j in range(0,i):
if nums[i]>nums[j]:
tem=child[j]+1
if tem>best_i:
best_i=tem
child.append(best_i)
return max(child)
方法2: 贪心+二分查找:看官方题解。贪心算法的核心思想是寻找局部最优。时间复杂度 O(NlogN)。这道题只输出长度,所以可以用此算法代替,如要输出序列,则不能如此。
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
# 贪心+二分查找 O(NlogN)
if nums == []:
return 0
tail=[nums[0]] # 存长度最长上升子序列的最后一个元素值
len=1 # 最长上升子序列的长度
for num in nums[1:]:
if num>tail[len-1]:
tail.append(num)
len+=1
else:
# 二分查找比num大的最小的元素及位置
l=0
r=len-1
while l<r:
mid = int((l+r)/2)
if tail[mid] == num:
l=mid
break
if tail[mid]<num:
l=mid+1
continue
elif tail[mid]>num:
r=mid
continue
tail[l]=num
return len
ps. 第二种方法不典型,着重理解第一种。
本博客为原创作品,欢迎指导,转载请说明出处,附上本文链接,谢谢