夯实算法-18.最长递增子序列的个数

276 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情

题目:LeetCode

给定一个未排序的整数数组 nums , 返回最长递增子序列的个数 。

注意 这个数列必须是 严格 递增的。

示例 1:

输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。

示例 2:

输入: [2,2,2,2,2]
输出: 5
解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5

提示: 

  • 1 <= nums.length <= 2000
  • -10^6 <= nums[i] <= 10^6

解题思路

根据题意分析:要求最长递增子序列的个数,最直接的方式就是遍历给定数组的所有元素,将以每个元素结尾的递增子序列的个数记录下来,同时记下以对应元素结尾的递增子序列出现的次数,通过这两个数组来统计出满足要求的结果;

  • lens[i]表示以nums[i]为结尾的递增子序列中元素的个数,time[i]表示以nums[i]为结尾的递增子序列出现的次数,遍历 nums 数组

遍历到nums[i]这个元素时,均要和i前面的元素进行比较(用变量j进行遍历),

  • 若nums[i]大于nums[j],说明nums[i]可以接到nums[j]后面,产生更长的递增子序列
  • 若lens[i] < lens[j] +1,更新lens数组边界 lens[i] = lens[j]+1,且times[i] = times[j]

若lens[i]恰好等于lens[j] +1,表示已经有和当前长度相同的子序列,更新times[i] += times[j],表示以nums[i]为结尾的子序列(长度为lens[i])已经出现过,要加上出现的次数。
最终统计出最大长度出现的次数,返回即可

代码实现

public int findNumberOfLIS(int[] nums) {
    if(nums.length == 0) {  // 对数组元素个数校验
        return 0;
    }
    int[] lens = new int[nums.length];  //以nums[i]为结尾的递增子序列元素个数
    int[] times = new int[nums.length]; //以nums[i]为结尾的递增子序列出现的次数 
    int maxLen = 1;
    for(int i = 0; i < nums.length; ++i){
        lens[i] = 1;   // 先设定初始值
        times[i] = 1;  // 初始值
        for(int j = 0; j < i; ++j){
            if(nums[i] <= nums[j])  // 元素不是严格递增,则进入下一轮
                continue;
            if(lens[j] + 1 > lens[i]){
                lens[i] = lens[j] + 1;
                times[i] = times[j];
            } else if(lens[j] + 1 == lens[i]){
                times[i] += times[j];
            }
        }
        maxLen = Math.max(maxLen, lens[i]); // 取更大值
    }
    int res = 0;
    for(int i = 0; i < lens.length; ++i){
        if(lens[i] == maxLen) {
            res += times[i];
        }
    }
    return res;  // 返回最大长度出现的次数最优值
}

运行结果

Snipaste_2022-08-27_22-32-30.png

复杂度分析

  • 时间复杂度:O(n2)O(n^2)
  • 空间复杂度:O(n)O(n) 需要创建俩个数组来维护

掘金(JUEJIN)  一起分享知识, Keep Learning!