300.最长递增子序列

😄300.最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

思路

基本思想

当前位置的最长递增子序列的长度肯定和之前的元素有关,所以考虑动态规划,主要是递推方程的确定,本题中dp[j]代表第j个元素之前有多少个元素比当前元素大,包括本身,因为单独一个元素,递增子序列长度为1,换句话说就是以nums[i]结尾的最长递增子序列长度

对当前元素来说,前面有很多元素,只要比其中的某一个元素大,都算是递增,此时递增子序列的长度就有可能增加,但是也不是肯定增加,举例来说,[4,3,5,6,2,8]中dp[8]=4而不是2,因为dp[8]取的是前面的元素中最长的递增子序列长度,而不是遇到一个比他小的就更新

最终的递推方程为: $$ dp[i]=max(dp[i],dp[j]+1) $$ 要看当前更新是不是能变大

不能直接使用一下判断语句更新dp,这个判断语句是连续递增时可以使用:

1
2
3
if(nums[i]>nums[i-1]){
    dp[i]=dp[i-1]+1;
}

这样在例子[4,10,4,3,8,9]中会出错,元素9比元素8大,但是他们两个的dp是一样的,因为前面有一个元素10,导致他们不能随意的增加

在之前的元素中找出最长的递增子序列,当前元素之前的所有比当前元素小的元素组成了最长的递增子序列

执行流程

  1. 初始化dp数组,初始值为1,因为[7,7,7,7]的最终结果为1

  2. 按照递推方程进行更新

  3. 对于每一个元素,找出以它结尾的最长递增子序列

  4. 返回最终结果

    由于最后一个元素不一定是全局最大的元素,所以dp数组的最后一个元素保存的不是最终的结果,需要找出dp数组中最大的值作为最终的结果

代码

根据以上分析,得出以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size()==1)
            return 1;
        vector<int> dp(nums.size(),1);
        int res=0;
        for(int i=1;i<nums.size();++i){
            for(int j=0;j<i;++j){
                //找到了比当前元素小的元素,dp可能更新
                if(nums[i]>nums[j]){
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
            //当前元素统计完成之后,统计最终结果
            res=max(res,dp[i]);
        }
        return res;
    }
};

总结

确定好递推方程,判断当前元素之前有几个比他小的,更新当前元素结尾的最长递增子序列,最终返回最大的序列长度作为最终的结果

对于每个元素都判断之前有几个元素比他小,从而更新自己的最长递增子序列