Pygorithm项目中的最长递增子序列(LIS)算法解析
2025-07-08 07:33:23作者:何举烈Damon
什么是LIS问题
最长递增子序列(Longest Increasing Subsequence, LIS)问题是计算机科学中一个经典的动态规划问题。给定一个数字序列,我们需要找到其中最长的子序列,使得这个子序列中的元素严格递增。注意这里的子序列不要求连续,只要保持原有顺序即可。
举个例子,对于序列[10, 22, 9, 33, 21, 50, 41, 60, 80],最长的递增子序列是[10, 22, 33, 50, 60, 80],长度为6。
Pygorithm中的LIS实现分析
Pygorithm项目提供了一个清晰高效的LIS算法实现,下面我们来详细解析其代码结构和算法原理。
算法实现
def longest_increasing_subsequence(_list):
# 初始化两个数组
lis = [1] * len(_list) # 存储以每个元素结尾的LIS长度
elements = [0] * len(_list) # 用于回溯构建LIS序列
# 动态规划填充lis数组
for i in range(1, len(_list)):
for j in range(0, i):
if _list[i] > _list[j] and lis[i] < lis[j] + 1:
lis[i] = lis[j]+1
elements[i] = j
# 找到最长子序列的长度和结束位置
maximum = max(lis)
idx = lis.index(maximum)
# 回溯构建最长子序列
seq = [_list[idx]]
while idx != elements[idx]:
idx = elements[idx]
seq.append(_list[idx])
return (maximum, seq[::-1])
算法步骤解析
-
初始化阶段:
lis
数组初始化为全1,表示每个元素自身至少可以构成长度为1的递增子序列elements
数组用于记录每个位置的前驱元素索引,初始为0
-
动态规划填充阶段:
- 使用双重循环,外层循环遍历每个元素
- 内层循环检查当前元素之前的所有元素
- 如果当前元素大于之前的某个元素,并且可以构成更长的子序列,则更新lis值和前驱索引
-
结果提取阶段:
- 找出lis数组中的最大值,即最长递增子序列的长度
- 从最大值对应的位置开始,通过前驱索引回溯构建完整的子序列
- 最后将序列反转得到正确的顺序
时间复杂度分析
该实现使用了双重循环,时间复杂度为O(n²),其中n是输入序列的长度。这是LIS问题的经典解法,对于中等规模的数据集表现良好。
实际应用示例
让我们通过一个具体例子来理解算法的工作过程:
输入序列:[3, 4, -1, 0, 6, 2, 3]
算法执行过程:
- 初始化lis = [1,1,1,1,1,1,1]
- 逐步填充lis数组:
- i=1: 3<4 → lis=[1,2,1,1,1,1,1]
- i=2: -1<3, -1<4 → 但lis值不增加
- i=3: 0>3, 0>4, 0>-1 → lis[3]=lis[2]+1=2
- 依此类推...
- 最终lis=[1,2,1,2,3,3,4]
- 最大值为4,对应序列[-1,0,2,3]
算法优化空间
虽然这个实现已经很清晰,但在某些情况下可以考虑优化:
- 对于大规模数据,可以使用O(nlogn)的二分查找优化版本
- 可以只返回长度而不需要具体序列,减少内存使用
- 可以添加输入验证,确保输入是有效的数字序列
总结
Pygorithm中的LIS实现展示了动态规划解决问题的典型模式:将大问题分解为子问题,存储中间结果避免重复计算,最后通过回溯构建完整解。这个实现不仅正确高效,而且代码结构清晰,非常适合学习动态规划的基本思想。
对于想要深入理解动态规划的开发者来说,研究这个实现是一个很好的起点。通过分析其工作原理并尝试自己实现,可以更好地掌握动态规划这一重要的算法设计范式。