1 题目
题目:最长上升子序列LIS(Longest Increasing Subsequence)
描述:给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。
最长上升子序列的定义:
最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,这种子序列不一定是连续的或者唯一的。
lintcode题号——76,难度——medium
样例1:
输入:nums = [5,4,1,2,3]
输出:3
解释:LIS 是 [1,2,3]
样例2:
输入:nums = [4,2,4,5,3,7]
输出:4
解释:LIS 是 [2,4,5,7]
2 解决方案
2.1 思路
使用动态规划的方式解,将状态定义为表示跳到位置i的最长上升子序列长度,则在位置i能够与位置j构成更长的上升序列的条件下,位置i的最优值 = max(位置j的最优值 + 1, 上次记录的位置i的最优值),理清动态规划的四要素即可解出。
循环结束后,dp中末尾位置的值表示的是以位置i为尾部的最长上升序列的序列长度,题意要求的是所有最长上升序列的最大长度,所以答案不在dp数组中,而需要在循环中打擂台获得。
2.2 时间复杂度
本题动态规划的多重循环是对同一维的下标进行的,相当于在一个耗时n的循环内嵌套了同为耗时n的循环,时间复杂度为O(n^2)。
2.3 空间复杂度
使用了容量为n的数组,空间复杂度为O(n)。
3 源码
细节:
- 动态规划的四要素:状态、方程、初始化、答案。(四要素在之前的题目数字三角形1中有详细介绍)
- 状态:用
dp[i]
表示跳到位置i的最长上升子序列长度(该上升序列最后一个值即位置i上的值)。 - 方程:在位置i能够与位置j构成更长的上升序列的条件下,
位置i的最优值 = max(位置j的最优值, 位置i原有的最优值)
。 - 初始化:所有的位置都以自身构成为长度为1的上升序列。
- 答案:dp[i]中保存的是以位置i为尾部最长上升序列的序列长度,而题意要求的是所有最长上升序列的最大长度,答案不在dp数组中,而需要在循环中打擂台获得。
C++版本:
/**
* @param nums: An integer array
* @return: The length of LIS (longest increasing subsequence)
*/
int longestIncreasingSubsequence(vector<int> &nums) {
// write your code here
if (nums.empty())
{
return 0;
}
// 状态:dp[i]表示跳到位置i的最长上升子序列长度(该上升序列最后一个值即i位置的值)
// 初始化:所有的位置都以自身构成为长度为1的上升序列
vector<int> dp(nums.size(), 1);
// 因为最长上升序列不一定都是以数组最后一个数结尾,需要在循环外定义一个值保存在过程中比较出的最大值
int maxLISLength = 0;
for (int i = 0; i < dp.size(); i++)
{
for (int j = 0; j < i; j++)
{
// 方程:在位置i能够与位置j构成更长的上升序列的条件下,位置i的最优值 = max(位置j的最优值, 位置i原有的最优值)
if (nums[i] > nums[j])
{
dp[i] = max(dp[j] + 1, dp[i]);
}
}
maxLISLength = max(dp[i], maxLISLength); // 取以位置i结尾的最长序列长度和之前的最优值中较大者
}
return maxLISLength; // 答案:dp[i]中保存的是以位置i为尾部的最长上升序列的序列长度,题意是所有最长上升序列的最大长度
}
数字三角形:https://blog.csdn.net/SeeDoubleU/article/details/124678103 ↩︎