题目
难度:★★★☆☆
类型:数组
方法:动态规划,二分法
传送门
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
解答
解法1:动态规划
这里跟以往不一样的是,需要注意一下上升子序列的定义,这里的子序列可以是间断的,这就加大了这个问题的难度。
我们用动态规划来解决这个问题,定义数组dp,长度与输入数组nums保持一致,dp[i]所代表的含义是以nums[i]结尾的所有上升子序列的最大长度。例如在数组[1, 7, 2, 3, 4, 9]中,[1, 2, 3, 9]为一个到9的上升子序列,[1, 7, 9]也是一个到9的上升子序列,但是前者更长。
定义初始状态,以dp[i](0<=i 递归关系式:为了求得到达数组中特定位置dp[i]的最长上升子序列的长度,我们需要考虑到在此之前的所有最长上子序列,也就是dp[i]的计算需要依据dp[j](j<=0
dp[i] = max(dp[i], dp[j]+1),条件是 nums[j] < nums[i] 为什么dp[j]+1可以算作到达dp[i]的一个上升子序列的长度?因为当nums[i] 最终结果:由于我们并不知道最长上升子序列在什么位置结尾,因此需要对dp中所有元素取最大值。 贪心算法有一个思路,就是最长上升子序列一定是上升最慢的方向。 准备一个数组d用于存储上升子序列,每当获取一个元素,我们判断一下这个元素和d中最后一个元素谁更大,如果该元素更大,则添加到d数组中,否则,找到数组d中该元素的位置并插入,数组d始终是有序的,所以可以使用二分查找。 如有疑问或建议,欢迎评论区留言~class Solution:
def lengthOfLIS(self, nums):
if not nums:
return 0
dp = [1 for _ in range(len(nums))]
for i in range(1, len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j]+1)
return max(dp)
贪心算法。
from bisect import bisect_left
class Solution:
def lengthOfLIS(self, nums):
d = []
for n in nums:
if not d or n > d[-1]:
d.append(n)
else:
d[bisect_left(d, n)] = n
return len(d)