目录
前言
最长递增子序列(Longest Increasing Subsequence,简称 LIS)问题是动态规划领域的一个经典问题。这个问题要求在一个给定的整数序列中找到一个最长的递增子序列。这里的“递增”意味着子序列中的每个元素都比前一个元素大。
问题定义
给你一个整数数组 nums
,找到其中最长严格递增子序列的长度。子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7]
是数组 [0,3,1,6,2,2,7]
的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7]
输出:1
提示:
1 <= nums.length <= 2500
-104 <= nums[i] <= 104
思路
对于最长递增子序列(LIS)问题,我选用动态规划方法解题。这种方法适用于具有重叠子问题和最优子结构特性的问题,而LIS问题正好满足这两个条件。
解题过程
状态定义
在动态规划中,我们定义
dp[i]
表示以第i
个元素结尾的最长递增子序列的长度。这个状态可以由更小的子问题推导出来。初始化
由于每个元素自身可以构成一个长度为1的递增子序列,我们可以初始化
dp
数组的所有元素为1。状态转移方程
对于每个
i
(从1到n-1
),我们检查i
之前的所有元素j
(从0到i-1
)。如果nums[j] < nums[i]
,则nums[i]
可以接在j
的后面形成一个更长的递增子序列。此时,更新dp[i]
为max(dp[i], dp[j] + 1)
。计算结果
遍历结束后,
dp
数组中的最大值就是整个数组的最长递增子序列的长度。
动态规划算法流程
- 初始化
dp
数组,长度与输入数组相同,所有元素设为1。- 遍历数组,对于每个元素
nums[i]
:对于j
从0到i-1
:如果nums[j] < nums[i]
,则尝试更新dp[i]
。- 计算
dp
数组中的最大值,即为所求的最长递增子序列的长度。
复杂度
时间复杂度
O(n^2)
,其中 n
是输入数组的长度。这是因为我们有两层嵌套循环,外层循环遍历数组的所有元素,内层循环用于查找可以构成递增子序列的元素。
空间复杂度
O(n)
,用于存储 dp
数组。
code
def lengthOfLIS(nums):
if not nums:
return 0
n = len(nums)
dp = [1] * n # 初始化dp数组
# 动态规划填表
for i in range(1, n):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
# 求dp数组中的最大值
return max(dp)