LeetCode 300:最长递增子序列

本文介绍了如何使用动态规划和贪心策略解决LeetCode中的最长递增子序列问题。通过实例演示了两种方法:一是标准的动态规划O(n^2)复杂度,二是利用贪心思想和最小堆优化至O(nlgn)。重点在于理解算法背后的逻辑和空间优化技巧。
摘要由CSDN通过智能技术生成

LeetCode 300:最长递增子序列

题目描述

给你一个整数数组 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
  • -10^4 <= nums[i] <= 10^4

方法1

题目想要求一个数组的最长递增子序列的长度,我们可以采用动态规划的思想,假设数组长度为len,维护一个len长的数组ansans[i]表示前i+1个元素(元素下标从0开始)的最长子序列长度,要求出ans[i],就需要让第i个元素值与第j个元素值依次进行比较(j >= 0 && j <= i-1),若第i个元素值较大则ans[i] = max(ans[i], ans[j] + 1)。最终求ans数组中的最大值即可。

算法复杂度:O(n * n)

int lengthOfLIS(vector<int>& nums) {
    int len = nums.size(), ans = 0;
    vector<int> dp(len, 1);
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < i; j++) {
            if(nums[i] > nums[j])
                dp[i] = max(dp[i], dp[j] + 1);
        }
        ans = max(ans, dp[i]);
    }
    return ans;
}

方法2

假设nums = [1, 4, 2, 5, 3],我们肉眼就能看出最长子序列长度为3,有两个符合的子序列,分别是[1, 2, 3][1, 4, 5]。假如在数组后再加一个元素4呢?数组nums = [1, 4, 2, 5, 3, 4],也很明显答案是4,我们在上面的第一个子序列[1, 2, 3]的基础上,追加一个元素4,就是我们的最长子序列。

从上面的例子可以看出,我们每次计算ans[i]时(ans[i]表示前i+1项的最长子序列长度,元素下标从0开始),希望前面子序列最长的情况下各项元素尽可能小,也就是用贪心的思想。

因此我们可以维护一个动态数组d,用于存放元素值最小的“最长子序列”。也就是当nums = [1, 4, 2, 5, 3]时,d = [1, 2, 3]

那我们应该如何遍历nums的同时来维护这个动态数组d呢?

每次将nums[i]d的最后一个元素比大小,若nums[i]大的话,直接在d中追加一个元素nums[i],并且ans[i]也就等于d的长度;若nums[i]小的话,则在d中用二分查找找到第一个比nums[i]大的元素,将其覆盖,ans[i]就是nums[i]d中的下标加一(因为下标从0开始)。

到这里理解了基本思路,可以进一步进行空间上的优化,题目所求的是最长子序列长度,不需要设置ans数组来记录前i个元素的最长子序列长度,因为数组d的长度就是最长子序列的长度。

算法复杂度:O(nlgn)

int lengthOfLIS(vector<int>& nums) {
    vector<int> d;
    int len = nums.size();
    for(int i = 0; i < len; i++) {
        if(d.empty() || nums[i] > d.back()) {
            d.push_back(nums[i]);
        }
        else {
            int t = lower_bound(d.begin(), d.end(), nums[i]) - d.begin();
            d[t] = nums[i];
        }
    }
    return d.size();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值