最长递增子序列(LIS)是算法面试高频基础题,也是动态规划入门的经典题型。很多人容易混淆子序列和子数组,子序列不要求连续、仅保留元素相对顺序,这也是本题的核心考点。
对于本题,在数据范围长度≤2500的前提下,O(n²)的动态规划解法是足够的
核心解题思路
解题关键是反向递推:不全局找最长序列,而是逐个确定「以当前元素结尾的最长递增子序列长度」。
我们定义 dp[i] :以数组第 i 个元素结尾的最长严格递增子序列长度。 初始状态很简单:每个元素自身就是一个长度为1的子序列,所以 dp 数组全部初始化为1。
状态转移逻辑清晰易懂:遍历每个元素 nums[i] ,再回头对比它之前的所有元素 nums[j] 。如果 nums[i] > nums[j] ,说明当前元素可以接在 nums[j] 的递增子序列后,此时更新 dp[i] = Math.max(dp[i], dp[j] + 1) 。
全程遍历结束后, dp 数组中的最大值,就是整个数组的最长递增子序列长度。
JavaScript 简洁代码
var lengthOfLIS = function(nums) {
const n = nums.length;
let dp = new Array(n).fill(1);
let maxLen = 1;
for(let i = 1;< n; i++){
for(let j = 0; j < i; j++){
if(nums[i] > nums[j]){
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
maxLen = Math.max(maxLen, dp[i]);
}
return maxLen;
};
复杂度与刷题总结
时间复杂度:O(n²),双重循环遍历数组,适配题目数据范围。 空间复杂度:O(n),额外开辟dp数组存储状态。
这套解法没有复杂逻辑,核心就是拆分最优子结构,把全局难题拆解为每个位置的局部最优解