#300
Medium
动态规划

Longest Increasing Subsequence

求最长严格递增子序列的长度。

求最长严格递增子序列长度。这题的优化版很经典,因为它不直接维护子序列本身,而是维护“每个长度下最小可能结尾”。

ArrayDPBinary Search

题目定位

它的经典入门解法是 DP,因为每个位置都在问“以我结尾的最长递增子序列是多少”;而进阶写法又能自然过渡到二分优化前沿状态。

关键观察

二分优化版并不真的存一条完整子序列,而是维护“每个长度下最小可能结尾”,这样才能给后续元素留下最大的接续空间。

目标复杂度

O(n log n) / O(n)

这题的解法思路怎么拆

1

维护 tails[i],表示“长度为 i + 1 的递增子序列,最小可能的结尾值”。

2

对每个数字,在 tails 里二分找第一个大于等于它的位置。

3

用当前数字替换该位置,因为更小的结尾只会让后续更容易接上。

4

如果当前数字比所有 tails 都大,就把它追加到末尾,表示 LIS 长度增加了。

拿一个例子顺一遍

1

例如 nums = [10, 9, 2, 5, 3, 7, 101, 18]。

2

tails 的变化是 [10] -> [9] -> [2] -> [2, 5] -> [2, 3] -> [2, 3, 7] -> [2, 3, 7, 101] -> [2, 3, 7, 18]。

3

最终 tails 长度为 4,因此 LIS 长度就是 4。

参考实现

Python
from bisect import bisect_left

def lengthOfLIS(nums: list[int]) -> int:
    tails: list[int] = []

    for value in nums:
        index = bisect_left(tails, value)
        if index == len(tails):
            tails.append(value)
        else:
            tails[index] = value

    return len(tails)

常见坑点

warning

把 subsequence 和连续子数组、子串混为一谈。

warning

把 tails 数组误当成真实答案序列,导致解释错误。

高频追问

如果面试官先要朴素解,你会怎样从 O(n^2) DP 讲到优化版?

如果要求输出具体子序列,还需要补哪些信息?

继续刷相关题

先把 动态规划 这个模式刷成体系,再去做更难变体。

view_week回到模式页
LeetCode 300. Longest Increasing Subsequence 题解思路 | Interview AiBox