300. 最长上升子序列

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

示例:

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

可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n)?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法一:动态规划

此题与前两篇博客都属于子序列问题,所以可以采用类似的动态规划方法解决,这种方法的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。具体做法是假设 d p [ i ] dp[i] dp[i]表示前 i i i个元素中的最长子序列长度,然后对于 n u m s [ i ] nums[i] nums[i]需要比较他与前 i − 1 i-1 i1个元素的大小,如果大于则则 d p [ i ] dp[i] dp[i]应该更新。转移方程为 d p [ i ] = m a x ( d p [ j ] ) + 1 dp[i]=max(dp[j])+1 dp[i]=max(dp[j])+1

代码

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int N = nums.size();
        if(!N) return 0;
        vector<int> dp(N, 1);

        for(int i = 0; i<N; ++i)
        {
            for(int j=0; j<i; ++j)
            {
                if(nums[j] < nums[i])
                {
                    dp[i] = max(dp[i], dp[j]+1);
                }
            }
        }
        return *max_element(dp.begin(), dp.end());
    }
};

方法二:贪心+二分查找

上述的动态规划,似乎是这类题目的一个通解,但是针对具体的题目也可以有一些优化,这一题题目要求优化到时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),看到这个复杂度要求的第一眼就想到了二分查找的时间复杂度是 l o g n logn logn,那么也就是用了 n n n次二分查找。但是二分查找又是需要在一个有序的序列中才能使用。

设当前已求出的最长上升子序列的长度为 len \textit{len} len初始时为 1),从前往后遍历数组 nums \textit{nums} nums,在遍历到 nums [ i ] \textit{nums}[i] nums[i]时:

  1. 如果 nums [ i ] > d [ len ] \textit{nums}[i] > d[\textit{len}] nums[i]>d[len] ,则直接加入到 d 数组末尾,并更新 len = len + 1 \textit{len} = \textit{len} +1 len=len+1
  2. 否则,在 d d d 数组中二分查找,找到第一个比 nums [ i ] \textit{nums}[i] nums[i] 小的数 d [ k ] d[k] d[k] ,并更新 d [ k + 1 ] = nums [ i ] d[k + 1] = \textit{nums}[i] d[k+1]=nums[i]

以输入序列 [ 0 , 8 , 4 , 12 , 2 ] [0, 8, 4, 12, 2] [0,8,4,12,2] 为例:

第一步插入 0 0 0 d = [ 0 ] d = [0] d=[0]

第二步插入 8 8 8 d = [ 0 , 8 ] d = [0, 8] d=[0,8]

第三步插入 4 4 4 d = [ 0 , 4 ] d = [0, 4] d=[0,4]

第四步插入 12 12 12 d = [ 0 , 4 , 12 ] d = [0, 4, 12] d=[0,4,12]

第五步插入 2 2 2 d = [ 0 , 2 , 12 ] d = [0, 2, 12] d=[0,2,12]

最终得到最大递增子序列长度为 3 3 3

代码

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len = 1, n = (int)nums.size();
        if (n == 0) return 0;
        vector<int> d(n + 1, 0);
        d[len] = nums[0];
        for (int i = 1; i < n; ++i) {
            if (nums[i] > d[len]) d[++len] = nums[i];
            else{
                int l = 1, r = len, pos = 0; // 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 pos 设为 0
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (d[mid] < nums[i]) {
                        pos = mid;
                        l = mid + 1;
                    }
                    else r = mid - 1;
                }
                d[pos + 1] = nums[i];
            }
        }
        return len;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值