动态规划:最长递增子序列
给定一个数组,求最长递增子序列的长度,就是要求我们求出一个序列中最长的上升子序列的长度,最长上升子序列的定义就是从原序列中按照孙旭去除一些数字,这些数字是逐渐增大的。
*定义dp[i]表示以第i个元素结尾的最长上升子序列的长度。
*初始时,每个dp[i]的值至少为1,因为每个元素本身就是一个长度为1的上升子序列
*对于每个元素i,我们遍历起前面的所有元素j,如果nums[i] < nums[j],则更新
dp[i] = max(dp[i],dp[j] + 1)。
*最终,最长上升子序列的长度就是dp数组中的最大值。
函数逻辑
-
输入与边界处理:
-
输入为整数向量
nums
。 -
若数组为空(
n == 0
),直接返回 0。
-
-
动态规划初始化:
-
创建长度为
n
的数组dp
,初始值全为 1。dp[i]
表示以nums[i]
结尾的最长递增子序列的长度(每个元素自身至少构成长度为 1 的子序列)。
-
-
动态规划递推:
-
外层循环遍历每个元素
nums[i]
(从第 2 个元素开始)。 -
内层循环遍历
nums[i]
之前的所有元素nums[j]
(j < i
)。 -
若
nums[j] < nums[i]
,说明可以将nums[i]
接在nums[j]
对应的递增子序列后。此时更新dp[i]
为max(dp[i], dp[j] + 1)
,确保dp[i]
记录当前最长长度。
-
-
返回结果:
-
最终返回
dp
数组中的最大值,即整个数组的最长递增子序列长度。
-
示例验证
以输入 [10, 9, 2, 5, 3, 7, 101, 18]
为例:
-
dp
数组逐步更新为[1, 1, 1, 2, 2, 3, 4, 4]
。 -
最大值为 4,对应最长递增子序列如
[2, 5, 7, 101]
。
复杂度分析
-
时间复杂度:O(n²),两层嵌套循环遍历所有元素对。
-
空间复杂度:O(n),用于存储
dp
数组。
#include <iostream>
#include <vector>
#include <algorithm> // 用于max_elementusing namespace std;/*** 使用动态规划求最长递增子序列长度* @param nums 输入数组* @return 最长递增子序列的长度*/
int lengthOfLIS(vector<int>& nums) {int n = nums.size();if (n == 0) return 0;// dp[i]表示以nums[i]结尾的最长递增子序列的长度vector<int> dp(n, 1); // 初始化为1,每个元素自身就是一个子序列for (int i = 1; i < n; ++i) {for (int j = 0; j < i; ++j) {if (nums[j] < nums[i]) {// 如果nums[j] < nums[i],则可以扩展子序列dp[i] = max(dp[i], dp[j] + 1);}}}// 返回dp数组中的最大值return *max_element(dp.begin(), dp.end());
}/*** 输出最长递增子序列本身(而不仅仅是长度)* @param nums 输入数组* @return 最长递增子序列*/
vector<int> getLIS(vector<int>& nums) {int n = nums.size();if (n == 0) return {};vector<int> dp(n, 1);vector<int> prev(n, -1); // 用于记录前驱元素索引for (int i = 1; i < n; ++i) {for (int j = 0; j < i; ++j) {if (nums[j] < nums[i] && dp[j] + 1 > dp[i]) {dp[i] = dp[j] + 1;prev[i] = j; // 记录前驱}}}// 找到dp数组中最大值的索引int max_len = 0, max_index = 0;for (int i = 0; i < n; ++i) {if (dp[i] > max_len) {max_len = dp[i];max_index = i;}}// 回溯构建LISvector<int> lis;while (max_index != -1) {lis.push_back(nums[max_index]);max_index = prev[max_index];}reverse(lis.begin(), lis.end()); // 反转得到正确顺序return lis;
}int main() {vector<int> nums = {10, 9, 2, 5, 3, 7, 101, 18};// 计算并输出最长递增子序列长度int length = lengthOfLIS(nums);cout << "最长递增子序列长度: " << length << endl;// 获取并输出最长递增子序列本身vector<int> lis = getLIS(nums);cout << "最长递增子序列: ";for (int num : lis) {cout << num << " ";}cout << endl;return 0;
}