返回
剑指 Offer 300:最长递增子序列
前端
2023-09-29 15:20:23
引子
在计算机科学领域,「最长递增子序列(LIS)」问题是一个经典且重要的算法问题。它要求我们从一个给定的序列中找到一组元素,使得该组元素按其在序列中的出现次序递增,且长度最长。理解 LIS 算法对于解决一系列与子序列相关的实际问题至关重要。
问题剖析
给定一个未经排序的序列 nums
,我们希望找出其最长递增子序列。可以将此问题看作是一个动态规划问题,因为它具有以下两个子问题最优解性质:
- 最优子结构: 最长递增子序列的子序列也是最长递增子序列。
- 重叠子问题: 对于序列中的每个元素,我们需要找到以该元素结尾的最长递增子序列。这可以通过查阅前面所有元素来完成。
动态规划算法
基于子问题最优解性质,我们设计如下动态规划算法:
- 初始化一个长度为
n
(nums
的长度)的数组dp
,其中dp[i]
表示以序列中第i
个元素结尾的最长递增子序列的长度。 - 对于
i
从 1 到n
,执行以下步骤:- 对于
j
从 0 到i-1
,执行以下步骤:- 如果
nums[i] > nums[j]
,则更新dp[i] = max(dp[i], dp[j] + 1)
。
- 如果
- 对于
- 返回
dp
中的最大值。
时间复杂度
该算法的时间复杂度为 O(n²),其中 n
是序列 nums
的长度。对于每个元素,我们需要检查它之前的每个元素,因此总共有 n²
次操作。
空间复杂度
该算法的空间复杂度为 O(n),因为我们需要一个长度为 n
的数组 dp
来存储子问题的结果。
示例
考虑序列 nums = [10, 22, 9, 33, 21, 50, 41, 60, 80]
。使用动态规划算法,我们得到以下 dp
数组:
dp = [1, 2, 1, 3, 2, 4, 3, 5, 6]
最长递增子序列的长度为 6
,该子序列为 [10, 22, 33, 50, 60, 80]
。
代码实现(Python)
def length_of_lis(nums):
"""
返回序列中递增子序列的最长长度。
参数:
nums: 未排序的序列。
返回:
递增子序列的最大长度。
"""
n = len(nums)
dp = [1] * n
for i in range(1, n):
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
总结
最长递增子序列问题是一个经典的算法问题,可以通过动态规划算法高效解决。该算法的时间复杂度为 O(n²),空间复杂度为 O(n)。理解 LIS 算法对于解决一系列子序列相关问题至关重要,在各种领域有着广阔的应用前景。