📜  最长递增子序列大小 (N log N)(1)

📅  最后修改于: 2023-12-03 15:10:37.941000             🧑  作者: Mango

最长递增子序列大小 (N log N)

介绍

最长递增子序列(Longest Increasing Subsequence,简称 LIS)问题是计算机科学领域中的一个经典问题,也是动态规划领域中的一个经典问题。给定一个长度为n的序列,要求找出一个最长的子序列使得这个子序列的元素是递增的。

最长递增子序列大小 (N log N) 是一种快速求解 LIS 问题的算法,其时间复杂度为 O(N log N)。该算法通过二分法优化寻找最长递增子序列的过程,因此效率较高。

算法思路

以输入序列 [2, 8, 9, 6, 7, 4] 为例,以下是算法的思路:

  1. 将一个长度为 n 的序列转换为长度相等的一个序列 B,其中 B[i] 代表原序列中以 i 结尾的最长上升子序列的长度。在未进行任何比较之前,B 中所有的值设为 1(因为每个元素都可以作为自己的最长上升子序列)。

    B: [1, 1, 1, 1, 1, 1]
    
  2. 从第二个元素开始遍历序列,对于每个元素,从序列开头向其前一个元素遍历,如果前面的元素小于当前元素,则更新 B[i] 为前面元素所在的最长上升子序列的长度加 1。

    B: [1, 2, 3, 2, 3, 2]
    
  3. 最终 B 中的最大值即为输入序列的最长递增子序列的长度。

通过上面的示例可以看出,该算法的基本思路是动态规划。但是,如果直接用动态规划求解 LIS 问题的时间复杂度是 O(n^2),无法满足较大规模的需求。因此,该算法引入了二分法的思想来减小计算量。具体实现如下:

  1. 定义一个数组 tails,talis[i] 表示长度为 i 的递增子序列的末尾元素的最小值。接着遍历输入序列,如果当前元素大于 tails 中的最大值,则将当前元素添加至 tails 末尾;否则,在 tails 中寻找第一个大于等于当前元素的元素并进行替换。

    tails: [2, 4, 6, 7, None, None]
    可以看到,tails 表示长度为 1 的递增子序列的末尾元素为 2,长度为 2 的递增子序列的末尾元素为 4,以此类推。
    
  2. tails 中的非 None 元素个数即为输入序列的最长递增子序列的长度。

代码实现

以下为 Python 代码实现:

def length_of_lis(nums):
    tails = [None] * len(nums)
    size = 0

    for num in nums:
        left, right = 0, size

        while left < right:
            mid = (left + right) // 2

            if tails[mid] < num:
                left = mid + 1
            else:
                right = mid

        tails[left] = num
        size = max(size, left + 1)

    return size
总结

最长递增子序列大小 (N log N) 算法通过二分法优化了计算 LIS 问题的过程,其时间复杂度为 O(N log N)。该算法是动态规划的实现思路,但相比传统的动态规划实现方式,它的效率更高、空间消耗更少。