leetcode 300. 最长上升子序列

问题描述

给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

思路一(动态规划)

设dp[i]代表以[nums[0],...,nums[i-1]]的最长上升子序列长度,此时时间复杂度O(N^2),空间复杂度O(N).

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
    int n = nums.size();
    if(n  < 2)return n;
    int i,j,val,maxnum=-1;
    vector<int> dp(n,1);//dp[i]代表以[nums[0],...,nums[i-1]]的最长上升子序列长度
    for(i = 0; i < n; i++)
    {
        val = nums[i];
        for(j = 0; j < i; j++)
        {
            if(nums[j] < val)
            {
                dp[i] = max(dp[i],dp[j]+1);//nums[i]可以接在nums[i]最长子序列后面形成新的最长子序列
            }
        }
        if(dp[i] > maxnum)maxnum = dp[i];
    }
    
    return maxnum;       
    }
};

结果

执行用时 :56 ms, 在所有 cpp 提交中击败了51.26%的用户
内存消耗 :8.7 MB, 在所有 cpp 提交中击败了15.03%的用户

思路二(二分查找)

新建数组 dp,用于保存最长上升子序列。对原序列进行遍历,将每位元素二分插入 dp 中。
如果 dp 中元素都比它小,将它插到最后;否则,用它覆盖掉比它大的元素中最小的那个。
总之,思想就是让 cell 中存储比较小的元素。这样,cell 未必是真实的最长上升子序列,但长度是对的。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
    int n = nums.size();
    if(n  < 2)return n;
    int i,end = 0,left,right,middle;
    vector<int> dp(n,INT_MIN);
    dp[0] = nums[0];
    for(i = 1; i < n; i++)
    {
        if(nums[i] > dp[end])
        {
            dp[++end]=nums[i];
        }
        else{
            left = 0;right = end;
            while(left < right)
            {
                middle = left + (right - left)/2;
                if(dp[middle] >= nums[i]){
                    right = middle ;
                }
                else{
                    left = middle + 1;
                }
            }
            dp[right] = nums[i]; 
        }
        
    }
    
    return end+1;       
    }
};

结果:

执行用时 :4 ms, 在所有 cpp 提交中击败了96.37%的用户
内存消耗 :8.5 MB, 在所有 cpp 提交中击败了71.57%的用户

最外层循环为O(N),内层为二分查找为O(log(N)),因此时间复杂度总计为O(Nlog(N)),空间复杂度为O(N)。

posted @ 2019-12-02 17:42  曲径通霄  阅读(312)  评论(0编辑  收藏  举报