声明:本文原题主要来自力扣,记录此博客主要是为自己学习总结,不做任何商业等活动!
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:输入:nums = [7,7,7,7,7,7,7]
输出:1
拿道题目,第一思路是先分析。
求数组最长上升子序列,也就是意味着要遍历整个数组中所有子序列,这时可以考虑是否可以用动态规划。由于动态规划知识,知道这是一题线性动态规划的题目,可以采用线性动态规划解法,即使经典的LIS(Longest Increasing Subsequence)。
动态规划满足三个条件:状态方程、状态转移方程、边界条件,根据分析可知该题满足条件,下面是分析的动态转移方程:
nums = [10, 9, 2, 5, 3, 7, 101, 18]
f(i) = max(f(j)) + 1, 0 <= i <= n, 0 <= j < i - 1; 满足条件nums[i] > nums[j];
i = 0, f(0) = 0;
i = 1, f(1) = 1;
i = 2, f(2) = max(f(1)) + 1;
i = n, f(n) = max(f(j)) + 1; 满足条件nums[i] > nums[j];
子序列个数n 1 2 3 4 5 6 7 8 元素值 10 9 2 5 3 7 101 18 LIS 1 1 1 2 2 3 4 4
由上面的状态转移方程和表格可知,需要求有n个子序列长度的LIS,就必须先求出第0个,然后退出第1个LIS,一直到该子序列最大长度。假如满足条件nums[i]>nums[j],每一个都在前面的基础上递加1.即关键实现代码如下:
for (int i = 0; i < size; ++i) // 遍历i个个数的子序列
{
dp[i] = 1;
for (int j = 0; j < i; ++j) // 求每个子序列的LIS
if (nums[i] > nums[j])
dp[i] = std::max(dp[i], dp[j] + 1); // 这里是关键,每次返回最大值
}
class Solution {
/*
nums = [10,9,2,5,3,7,101,18]
f(i) = max(f(j)) + 1 , 0 <= i <= n, 0 <= j < i - 1;满足条件nums[i]>nums[j];
i=0,f(0)=0;
i=1, f(1)=1;
i=2, f(2)=max(f(1))+1;
i=n, f(n)=max(f(j))+1;满足条件nums[i]>nums[j];
*/
public:
int lengthOfLIS(vector& nums) {
int size = nums.size();
if (!size) // 异常判断
return 0;
vector dp(size, 0); // 存储n个个数分别为1—n的子序列的LIS
for (int i = 0; i < size; ++i) // 遍历i个个数的子序列
{
dp[i] = 1;
for (int j = 0; j < i; ++j) // 求每个子序列的LIS
if (nums[i] > nums[j])
dp[i] = std::max(dp[i], dp[j] + 1); // 这里是关键,每次返回最大值
}
return *std::max_element(dp.begin(), dp.end()); // 返回最大值
}
};
1.4结果
由上面代码实现可知,经过了双层遍历,即时间复杂度为O(n^2);申请了n个长度的数组空间,故空间复杂度为O(n)。