leetcode300. 最长递增子序列(动态规划 贪心 二分)

链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/

题目

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

进阶:

你可以设计时间复杂度为 O(n2) 的解决方案吗?
你能将算法的时间复杂度降低到 O(n log(n)) 吗?

思路

方法一
动态规划 记录第一个位置到每一个位置最大距离 O(n^2)
注意*max_element(dp.begin(),dp.end());取数组最大值
转移方程dp[i]=max(dp[j]+1,dp[i]) ,nums[i]>nums[j],i>j

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        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());
    }
};

方法二
贪心
维护一个数组,记录在长度固定的情况下,结尾最小的那个元素的数值
对原序列进行遍历,将每位元素二分插入 cell 中。
如果 cell 中元素都比它小,将它插到最后
否则,用它覆盖掉比它大的元素中最小的那个。
思想就是让 cell 中存储比较小的元素。这样,cell 未必是真实的最长上升子序列,但长度是对的。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len=1,n=nums.size();
        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数组
                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;
    }
};
posted @ 2021-10-20 14:19  kitamu  阅读(69)  评论(0编辑  收藏  举报
Live2D