数组的最长递增子序列及进阶(C|swift)

268 阅读3分钟

数组的最长递增子序列


描述

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

-104 <= nums[i] <= 104

思路:动态规划
  • 定义数组 dp 存储子问题的最优解,dp[i] 表示以 以 nums[i] 结尾的子序列的最大长度
  • 遍历 nums 数组,求解 dp[i]

    • dp[i] 的默认值为 1,在 dp 中向前遍历比 nums[i] 小的数 nums[j] ,取 max(num[j] + 1)
  • 计算完 dp 后,nums 的最长升序子序列的长度为 dp 数组中的最大值

复杂度分析
  • 时间复杂度:O(n2) ,遍历数组为 n,计算状态 dp[i] 时,需要 O(n) 的时间遍历 dp[0...i-1] 的所有状态,所以总的时间为 O(n2)
  • 空间复杂度:O(n), 需要用到长度为 n 的 dp 数组。
题解( C )
 int lengthOfLIS(int* nums, int numsSize){
     //辅助数组:存储nums[i]结尾的升序子序列最大长度
     int dp[numsSize];
     int max = 0;
     for(int i = 0; i < numsSize; i++) {
         dp[i] = 1;
         for(int j = 0; j < i; j++) {
             //当 nums[i] > nums[j],表示 i 可以放在 j之后形成更长的升序子序列
             if(nums[i] > nums[j]) {
                 dp[i] = fmax(dp[j] + 1, dp[i]);
             }
         }
         max = fmax(max, dp[i]);
     }
     
     return max;
 }
题解( swift )
 class Solution {
     func lengthOfLIS(_ nums: [Int]) -> Int {
         var dp = Array(repeating: 1, count: nums.count)
         var maxA = 1
         for (i, value) in nums.enumerated() {
             for j in 0..<i {
                 if value > nums[j] {
                     dp[i] = max(dp[i], dp[j] + 1)
                 }
             }
             maxA = max(maxA, dp[i])
         }
         return maxA
     }
 }

进阶:求解最长递增子序列的个数


描述

给定一个未排序的整数数组,找到最长递增子序列的个数。

示例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。
约束条件

注意: 给定的数组长度不超过 2000 并且结果一定是32位有符号整数。

思路:动态规划
  • 在求解 数组的最长递增子序列 的基础进行题解

  • 新定义一个数组 cncn[i] 表示以 nums[i] 为结尾的最长升序子序列的个数

    • 遍历 nums 数组,计算 dp[i] 之后计算 cn[i]
    • cn[i] 的值为所有满足 dp[k] + 1 = dp[i] && nums[i] > nums[k] (0 <= k < i,)cn[k] 之和
    • 如果计算得出 cn[i] 为 0,就给它设置为 1
  • 计算完 cn 后,nums 的最长递增子序列的值为满足 dp[i] 值为最大升序子序列长度的 cn[i] 之和
复杂度分析
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n), 需要用到长度为 n 的 dp 数组和 cn 数组。
题解( C )
 int findNumberOfLIS(int* nums, int numsSize){
     if(numsSize <=1) {
         return 1;
     }
     int max = 1,res = 0;
     int dp[numsSize], cn[numsSize];
 ​
     for(int i = 0; i < numsSize; i++) {
         dp[i] = 1;
         for(int j = 0; j < i; j++) {
             if(nums[i] > nums[j]){
                 dp[i] = fmax(dp[i],dp[j]+1);
             }
         }
         max = fmax(dp[i],max);
         //令 cn[i] 初始为 0
         cn[i] = 0;
         //计算满足 dp[j] + 1 = dp[i] && nums[i] > nums[j] 的 cn[j] 的和
         for(int k = 0; k < i; k++) {     
             if(dp[i] == dp[k]+1 && nums[i] > nums[k]){
                 cn[i] += cn[k];
             }
         }
         //如果没有以 nums[i] 为结尾的升序子序列,就将其设置为 1
         if(cn[i] == 0) {
             cn[i] = 1;
         }
     }
 ​
     for(int i = 0; i<numsSize; i++) {
         if(dp[i] == max) {
             res += cn[i];
         }
     }
 ​
     return res;
 }
题解( swift )
 class Solution {
     func findNumberOfLIS(_ nums: [Int]) -> Int {
         var dp = Array(repeating: 1, count: nums.count)
         var cn = Array(repeating: 0, count: nums.count)
         var maxA = 1,res = 0
         for (i, value) in nums.enumerated() {
             for j in 0..<i {
                 if value > nums[j] {
                     dp[i] = max(dp[i], dp[j] + 1)
                 }
             }
             maxA = max(maxA, dp[i])
 ​
             for k in 0..<i {
                 if dp[k] + 1 == dp[i] && nums[i] > nums[k] {
                     cn[i] += cn[k]
                 }
             }
             cn[i] = cn[i] == 0 ? 1 : cn[i];
         }
 ​
         for (index, value) in cn.enumerated() {
             if dp[index] == maxA {
                 res += cn[index]
             }
         } 
 ​
         return res
     }
 }

实战练习

leedcode:300.数组的最长递增子序列

leedcode:673. 最长递增子序列的个数