Skip to main content

300.最长递增子序列

标签: array, binary-search, dynamic-programming

难度: Medium

通过率: 56.79%

原题链接: https://leetcode.com/problems/longest-increasing-subsequence/description/

题目描述

给定一个整数数组 nums,返回其中最长严格递增子序列的长度。

解题思路

解决这个问题可以有两种经典方法:动态规划和二分查找结合动态规划。

方法一:动态规划

我们定义一个数组 dpdp,其中 dp[i]dp[i] 表示以 nums[i]nums[i] 结尾的最长递增子序列的长度。对于每个元素 nums[i]nums[i],我们需要找到 j<ij<i 使得 nums[j]<nums[i]nums[j] < nums[i]dp[j]dp[j] 最大,然后 dp[i]=dp[j]+1dp[i] = dp[j] + 1。初始情况下,所有元素都是长度为1的子序列,因此 dp[i]dp[i] 初始化为1。

整个数组遍历完毕后,返回 dpdp 数组的最大值。

方法二:二分查找结合动态规划

我们假设一种更高效的途径,通过构建一个新数组 tailstails 来追踪当前子序列的最小结尾。

  1. 初始化一个空的 tailstails 数组。
  2. 对于每个 numnum
    • 使用二分查找在 tailstails 中寻找第一个大于或等于 numnum 的位置。如果找到了,那么用 numnum 更新这个位置;如果没有找到,就把 numnum 添加到 tailstails 的末尾。

这个过程保证了 tailstails 中的元素是递增的,最终 tailstails 的长度即为最长递增子序列的长度。

代码实现

def lengthOfLIS(nums):
# 动态规划方法
if not nums:
return 0
dp = [1] * len(nums) # 初始化dp数组
for i in range(len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp) # 返回dp数组中的最大值

复杂度分析

方法一:动态规划

时间复杂度:O(n2)O(n^2),其中 nn 是数组的长度,因为需要两层遍历。

空间复杂度:O(n)O(n),需要储存 dp 数组。

方法二:二分查找结合动态规划

时间复杂度:O(nlogn)O(n \log n),其中 nn 是数组长度。每个元素进行二分查找的复杂度为 O(logn)O(\log n)

空间复杂度:O(n)O(n),需要储存 tails 数组。