300. 最长递增子序列
Origin: LeetCode 300
题目描述
给定一个整数数组 nums,找到其中最长严格递增子序列的长度。子序列不要求连续,但要求顺序不变。
示例
Input: nums = [10, 9, 2, 5, 3, 7, 101, 18]
Output: 4
Explanation: 最长递增子序列是 [2, 3, 7, 101] 或 [2, 3, 7, 18],长度 4。
Input: nums = [0, 1, 0, 3, 2, 3]
Output: 4
Input: nums = [7, 7, 7, 7, 7, 7, 7]
Output: 1(严格递增,相同不算)
约束
- 1 <= nums.length <= 2500
- -10^4 <= nums[i] <= 10
关键信息
- 子序列,不要求连续
- 严格递增(相等不算)
- 朴素 DP O(n²):dp[i] = 以 nums[i] 结尾的 LIS 长度
- tails + 二分 O(n log n):之前做过 HackerRank 第 38 题
Resolution
我的解法(findIndex 版本,O(n²))
function lengthOfLIS(nums: number[]): number {
const n = nums.length
const tails = [nums[0]]
for (let i = 1; i < n; i++) {
if (nums[i] > tails[tails.length - 1]) {
tails.push(nums[i])
} else {
const insertIndex = tails.findIndex((tail) => tail >= nums[i])
tails[insertIndex] = nums[i]
}
}
return tails.length
}标准解法(二分查找,O(n log n))
function lengthOfLIS(nums: number[]): number {
const tails = [nums[0]]
for (let i = 1; i < nums.length; i++) {
if (nums[i] > tails[tails.length - 1]) {
tails.push(nums[i])
} else {
// 二分查找第一个 >= nums[i] 的位置
let left = 0
let right = tails.length - 1
while (left < right) {
const mid = Math.floor((left + right) / 2)
if (tails[mid] >= nums[i]) {
right = mid
} else {
left = mid + 1
}
}
tails[left] = nums[i]
}
}
return tails.length
}解题思路
tails + 二分查找。tails[i] 存”长度为 i+1 的递增子序列的最小末尾值”。
- 比末尾大 → push(扩展)
- 否则 → 找第一个 >= cur 的位置替换
朴素 DP(O(n²))
dp[i] = max(dp[j] + 1),对所有 j < i 且 nums[j] < nums[i]。
意思是:以 nums[i] 结尾的最长递增子序列长度 = 前面所有比 nums[i] 小的元素里,最大的 dp 值 + 1。
用 [10, 9, 2, 5, 3, 7] 走一遍:
i=0, nums[0]=10, 前面没有,dp[0]=1
i=1, nums[1]=9, 前面比 9 小的?没有(10 > 9),dp[1]=1
i=2, nums[2]=2, 前面比 2 小的?没有,dp[2]=1
i=3, nums[3]=5, 前面比 5 小的:nums[2]=2, dp[2]=1。dp[3]=1+1=2
i=4, nums[4]=3, 前面比 3 小的:nums[2]=2, dp[2]=1。dp[4]=1+1=2
i=5, nums[5]=7, 前面比 7 小的:nums[3]=5(dp=2), nums[4]=3(dp=2), nums[2]=2(dp=1)。取最大 dp[3]+1=3
最终答案 = max(dp) = 3。对每个 i 要扫描前面所有 j,O(n²)。tails + 二分把内层扫描优化到 O(log n)。
踩坑
- findIndex 是 O(n),n=2500 能过但大数据量要手写二分(HackerRank 第 38 题踩过)
- 严格递增用
>=找第一个大于等于的位置,不是>
复杂度
- 时间:O(n²)(findIndex 版本),手写二分可优化到 O(n log n)
- 空间:O(n)