首页
/ Pygorithm项目中的最长递增子序列(LIS)算法解析

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])

算法步骤解析

  1. 初始化阶段

    • lis数组初始化为全1,表示每个元素自身至少可以构成长度为1的递增子序列
    • elements数组用于记录每个位置的前驱元素索引,初始为0
  2. 动态规划填充阶段

    • 使用双重循环,外层循环遍历每个元素
    • 内层循环检查当前元素之前的所有元素
    • 如果当前元素大于之前的某个元素,并且可以构成更长的子序列,则更新lis值和前驱索引
  3. 结果提取阶段

    • 找出lis数组中的最大值,即最长递增子序列的长度
    • 从最大值对应的位置开始,通过前驱索引回溯构建完整的子序列
    • 最后将序列反转得到正确的顺序

时间复杂度分析

该实现使用了双重循环,时间复杂度为O(n²),其中n是输入序列的长度。这是LIS问题的经典解法,对于中等规模的数据集表现良好。

实际应用示例

让我们通过一个具体例子来理解算法的工作过程:

输入序列:[3, 4, -1, 0, 6, 2, 3]

算法执行过程:

  1. 初始化lis = [1,1,1,1,1,1,1]
  2. 逐步填充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
    • 依此类推...
  3. 最终lis=[1,2,1,2,3,3,4]
  4. 最大值为4,对应序列[-1,0,2,3]

算法优化空间

虽然这个实现已经很清晰,但在某些情况下可以考虑优化:

  1. 对于大规模数据,可以使用O(nlogn)的二分查找优化版本
  2. 可以只返回长度而不需要具体序列,减少内存使用
  3. 可以添加输入验证,确保输入是有效的数字序列

总结

Pygorithm中的LIS实现展示了动态规划解决问题的典型模式:将大问题分解为子问题,存储中间结果避免重复计算,最后通过回溯构建完整解。这个实现不仅正确高效,而且代码结构清晰,非常适合学习动态规划的基本思想。

对于想要深入理解动态规划的开发者来说,研究这个实现是一个很好的起点。通过分析其工作原理并尝试自己实现,可以更好地掌握动态规划这一重要的算法设计范式。