Leetcode 300 最长上升子序列 C++,Python,Java

Leetcode300 最长上升子序列

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence

博主Githubhttps://github.com/GDUT-Rp/LeetCode

题目:

给定一个无序的整数数组,找到其中最长上升子序列的长度。c

示例 1:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

说明:

  • 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
  • 你算法的时间复杂度应该为 O( n 2 n^2 n2) 。

解题思路:

方法一:带记忆的递归

直观想法

在前面的方法中,许多递归调用必须使用相同的参数进行一次又一次的调用。通过将为特定调用获得的结果存储在二维记忆数组 memo 中,可以消除这种冗余。 m e m o [ i ] [ j ] memo[i][j] memo[i][j] 表示使用 n u m s [ i ] nums[i] nums[i] 作为上一个被认为包含/不包含在 lis 中的元素的 lis 可能的长度,其中 n u m s [ j ] nums[j] nums[j] 作为当前被认为包含/不包含在 lis 中的元素。这里, n u m s nums nums 表示给定的数组。

C++
class Solution {
public:
    int lengthOfLIS(vector<int> &nums) {
        int len = nums.size();
        
    }
};
Java
public class Solution {
    public int lengthOfLIS(int[] nums) {
        int memo[][] = new int[nums.length + 1][nums.length];
        for (int[] l : memo) {
            Arrays.fill(l, -1);
        }
        return lengthofLIS(nums, -1, 0, memo);
    }
    public int lengthofLIS(int[] nums, int previndex, int curpos, int[][] memo) {
        if (curpos == nums.length) {
            return 0;
        }
        if (memo[previndex + 1][curpos] >= 0) {
            return memo[previndex + 1][curpos];
        }
        int taken = 0;
        if (previndex < 0 || nums[curpos] > nums[previndex]) {
            taken = 1 + lengthofLIS(nums, curpos, curpos + 1, memo);
        }

        int nottaken = lengthofLIS(nums, previndex, curpos + 1, memo);
        memo[previndex + 1][curpos] = Math.max(taken, nottaken);
        return memo[previndex + 1][curpos];
    }
}
Python
class Solution:
    def nthUglyNumber(self, n: int) -> int:
        

复杂度分析

时间复杂度: O ( n 2 ) O(n^2) O(n2)。递归树的大小可以达到 n 2 n^2 n2
空间复杂度: O ( n 2 ) O(n^2) O(n2),使用 n ∗ n n*n nn m e m o memo memo 数组。

方法二:动态规划

他的方法依赖于这样一个事实,在给定数组中最长上升子序列可能达到 i t h i^{th} ith
独立于后面在数组中出现的元素。因此,如果我们知道 lis 的长度不超过 i t h i^{th} ith,我们可以根据索引为 j j j 的元素包括 ( i + 1 ) t h (i+1)^{th} (i+1)th 元素来计算 lis 的长度,其中 0 ≤ j ≤ ( i + 1 ) 0\leq j\leq (i + 1) 0j(i+1)

我们使用 d p dp dp 数组来存储所需的数据。 d p [ i ] dp[i] dp[i] 表示考虑到数组元素一直到 i t h i^{th} ith 的情况下可能的最长上升子序列的长度,必须包括 i t h i^{th} ith 元素。为了找出 d p [ i ] dp[i] dp[i],我们需要尝试在每个可能的最长上升子序列中附加当前元素 ( n u m s [ i ] ) (nums[i]) (nums[i]) ( i − 1 ) t h (i-1)^{th} (i1)th(包括 ( i − 1 ) t h (i-1)^{th} (i1)th),这样通过添加当前元素形成的新序列也是一个上升子序列。因此,我们可以很容易地确定 d p [ i ] dp[i] dp[i] 使用:
d p [ i ] = max ( d p [ j ] ) + 1 , ∀ 0 ≤ j < i dp[i] = \text{max}(dp[j]) + 1, \forall 0\leq j < i dp[i]=max(dp[j])+1,0j<i
最后,确定最终结果的所有 d p [ i ] dp[i] dp[i] 中的最大值。

L I S l e n g t h = max ( d p [ i ] ) , ∀ 0 ≤ i < n LIS_{length}= \text{max}(dp[i]), \forall 0\leq i < n LISlength=max(dp[i]),0i<n

C++
class Solution {
public:
    int lengthOfLIS(vector<int> &nums) {
        int len = nums.size();
        if (len == 0) return 0;
        vector<int> dp(len, 1);
        dp[0] = 1;
        int maxans = 1;
        for (int i = 1; i < len; ++i) {
            int maxval = 0;
            for (int j = 0; j < i; ++j) {
                if (nums[i] > nums[j]) {
                    maxval = max(maxval, dp[j]);
                }
            }
            dp[i] = maxval + 1;
            maxans = max(maxans, dp[i]);
        }
        return maxans;
    }
};
Java
public class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }
        int[] dp = new int[nums.length];
        dp[0] = 1;
        int maxans = 1;
        for (int i = 1; i < dp.length; i++) {
            int maxval = 0;
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    maxval = Math.max(maxval, dp[j]);
                }
            }
            dp[i] = maxval + 1;
            maxans = Math.max(maxans, dp[i]);
        }
        return maxans;
    }
}
Python
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)。有两个 n n n的循环。
  • 空间复杂度: O ( n ) O(n) O(n),用了大小为 n n n 的矩阵 d p dp dp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值