动态规划——最长递增子序列(❗❗❗经典面试题,巨细讲解❗❗❗)

292 阅读3分钟

题目链接:leetcode.cn/problems/lo…

题目

给你一个整数数组 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

提示:

  • 1<=nums.length<=25001 <= nums.length <= 2500
  • 104<=nums[i]<=104-10^4 <= nums[i] <= 10^4

解析

1.状态表示

这题需要用到动态规划来解

dp[i]:最长递增子序列的长度

image.png 如上图,dp 表的下标位置和 arr 数组对应, dp[0]就代表 i = 0时的最大递增子序列长度,就为 1

i = 5时也就是 do[5] = 3,就代表最大递增子序列长度为 3……

2.状态转移方程

那我们如何来维护这个表呢?

image.png

我们看上图 dp[i] 的表达有两种

先解释一下j,将它定义在此的目的就是为了表达在i前面的一个数。

1.当arr[i]比前面任何数都小时,它只能为1,像i = 1, i = 2,也就是任何i前面的数 arr[j]都大于arr[i]

2.如果i下标之前存在有最大的dp[j],且满足arr[j] < arr[i],那么就是用第二个表达式,这么讲有点难读,我们直接上例子

拿上图的arr[5] = 7来说,它前面有5个 dp[j],分别是dp[0] = 1dp[1] = 1dp[2] = 1dp[3] = 2dp[4] = 2

其中 dp[3]dp[4]都等于2,那么我们肯定拿arr对应值小一点的 (因为小一点的值比大一点的值能续上这个递增序列的概率更大,比如如果后面不是7,而是4的话)dp[4](此时j = 4)满足 长度 2 是表中较大的元素,且arr[j]也就是arr[4] = 3 小于 arr[i] = 7 。同时满足这两个条件,就是用第二个表达式。

3.代码

时间复杂度:O(n2)O(n^2)
空间复杂度:O(n)O(n)

class Solution {
    public int lengthOfLIS(int[] nums) {
        //获取数组长度
        int n = nums.length;
        
        //1.定义dp表
        int[] dp = new int[n];
        
       	/*2.初始化dp表
       	此时为什么要初始化为1,我们不初始化默认的都是0,
       	初始化就是因为dp[i]最小值为1(最小的递增子序列长度就是1)
       	而且都初始化为1,就不需要再额外赋值dp表长度为1的情况,
       	看到下面的内循环就懂了
       	*/
        for (int i = 0; i < n; i++) {
            dp[i] = 1;
        }
        
        //为了提高代码效率,我们直接在此处定义最大数,待会在循环中更新就行
        int maxLength = 1;
        
        //3.核心代码,使用状态转移方程维护dp表
        for (int i = 0; i < n; i++) {//此处的外循环是为了给dp表赋值
            
            for (int j = 0; j < i; j++) {//内循环是为了找到dp[i]为多少
                
                //使用我们刚刚判断的条件,如果满足第二个表达式的话就能求出dp[i]
                if (nums[j] < nums[i]) {//if中的是判断是否满足前面的数比后面的数小
                    /*此处是使用for循环,找到 0 ~ i-1 这个范围中  满足if中条件的  最大的dp[j]
                    然后更新此处的dp[i]
                    */
                    dp[i] = Math.max(dp[j] + 1, dp[i]);
                }
            }
            //在外循环中更新 maxLength 的最大值
            maxLength = Math.max(dp[i] , maxLength);
        }
        
        return maxLength;
    }
}