动态规划 · O(n²) 算法 · 递推详解与逐步演示
nums[0..n-1],找到最长严格递增子序列的长度。dp[i] = 以 nums[i] 结尾的最长递增子序列的长度。max(dp[0], dp[1], ..., dp[n-1])
dp[i] = max(1, max(dp[j] + 1) for all j < i if nums[j] < nums[i])
dp[i] = 1i,遍历所有 j < i,如果 nums[j] < nums[i](严格递增),
就尝试将 nums[i] 接到以 nums[j] 结尾的子序列后面:dp[i] = max(dp[i], dp[j] + 1)prev[i] 记录 dp[i] 是从哪个 j 转移来的,用于重构 LIS 序列i = 0 → n-1,内层循环遍历 j = 0 → i-1,n(n-1)/2 = O(n²)。#include <vector> #include <algorithm> using namespace std; // 返回 LIS 的长度和序列 pair<int, vector<int>> lengthOfLIS(vector<int>& nums) { int n = nums.size(); if (n == 0) return {0, {}}; // dp[i]: 以 nums[i] 结尾的 LIS 长度 vector<int> dp(n, 1); // prev[i]: 前驱下标,用于回溯 LIS 序列 vector<int> prev(n, -1); int maxLen = 1; int maxIdx = 0; // 外层: 遍历每个位置 i for (int i = 0; i < n; i++) { // 内层: 遍历 i 之前的所有位置 j for (int j = 0; j < i; j++) { // 只有 nums[j] < nums[i] 才能形成递增 if (nums[j] < nums[i]) { // 尝试将 nums[i] 接到以 nums[j] 结尾的序列后面 if (dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1; prev[i] = j; // 记录从 j 转移来 } } } // 更新全局最大值 if (dp[i] > maxLen) { maxLen = dp[i]; maxIdx = i; } } // 回溯重构 LIS 序列 vector<int> lis; for (int cur = maxIdx; cur != -1; cur = prev[cur]) { lis.push_back(nums[cur]); } reverse(lis.begin(), lis.end()); // 逆序得到正序 return {maxLen, lis}; }
抖音视频讲解 —— LIS 最长递增子序列(DP 动态规划 O(n²) 和 O(n log n) 两种方法)