300. 最长上升子序列(Python)

题目

难度:★★★☆☆
类型:数组
方法:动态规划,二分法

传送门

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例

输入: [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中所有元素取最大值。

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)

贪心算法。

贪心算法有一个思路,就是最长上升子序列一定是上升最慢的方向。

准备一个数组d用于存储上升子序列,每当获取一个元素,我们判断一下这个元素和d中最后一个元素谁更大,如果该元素更大,则添加到d数组中,否则,找到数组d中该元素的位置并插入,数组d始终是有序的,所以可以使用二分查找。

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)

如有疑问或建议,欢迎评论区留言~

你可能感兴趣的:(300. 最长上升子序列(Python))