【错题集-编程题】最长上升子序列(二)(贪心 + 二分)

简介: 【错题集-编程题】最长上升子序列(二)(贪心 + 二分)

牛客对应题目链接:最长上升子序列(二)_牛客题霸_牛客网 (nowcoder.com)

力扣对应题目链接:300. 最长递增子序列 - 力扣(LeetCode)


一、分析题目

1、贪心 + 二分

在考虑最长递增子序列的长度的时候,其实并不关心这个序列长什么样子,我们只是关心最后⼀个元素是谁。这样新来⼀个元素之后,我们就可以判断是否可以拼接到它的后面。

因此,我们可以创建⼀个数组,统计长度为 x 的递增子序列中,最后一个元素是谁。为了尽可能的让这个序列更长,我们仅需统计长度为 x 的所有递增序列中最后一个元素的最小值。统计的过程中发现,数组中的数呈现递增趋势,因此可以使用二来查找插入位置。


2、动态规划

(1)dp[i] 的定义

表示 i 之前包括 i 的以 nums[i] 结尾的最长递增子序列的长度。


(2)状态转移方程

if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);


(3)初始化

每一个 i 对应的 dp[i](即最长递增子序列)起始大小至少都是 1。

(4)遍历顺序

从前往后遍历。


二、代码

1、贪心 + 二分(推荐)

//值得学习的代码
//O(NlogN)
class Solution
{
    int dp[100010] = { 0 }; // dp[i] 表⽰:⻓度为 i 的最⼩末尾
    int pos = 0;
 
public:
    int LIS(vector<int>& a) 
    {
        for(auto x : a)
        {
            // 查找 x 应该放在哪个位置
            if(pos == 0 || x > dp[pos])
            {
                dp[++pos] = x;
            }
            else
            {
                // ⼆分查找插⼊位置
                int l = 1, r = pos;
                while(l < r)
                {
                    int mid = (l + r) / 2;
                    if(dp[mid] >= x) r = mid;
                    else l = mid + 1;
                }
                dp[l] = x;
            }
        }
        return pos;
    }
};

2、动态规划

//力扣AC代码
//从前往后遍历(推荐)
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        if(n<=1) return n;
        vector<int> dp(n, 1);
        int res=0;
        for(int i=1; i<n; i++)
        {
            for(int j=0; j<i; j++)
            {
                if(nums[j]<nums[i])
                    dp[i]=max(dp[i], dp[j]+1);
            }
            if(dp[i]>res)
                res=dp[i];
        }
        return res;
    }
};
 
//从后往前遍历
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        vector<int> dp(n, 1);
        int res=0;
        for(int i=n-1; i>=0; i--)
        {
            for(int j=i+1; j<n; j++)
            {
                if(nums[i]<nums[j])
                    dp[i]=max(dp[i], dp[j]+1);
            }
            res=max(res, dp[i]);
        }
        return res;
    }
};
 
//记忆化搜索
class Solution {
public:
    int dfs(int pos, vector<int>& nums, vector<int>& memo)
    {
        if(memo[pos]!=0) return memo[pos];
        int ans=1;
        for(int i=pos+1; i<nums.size(); i++)
        {
            if(nums[i]>nums[pos])
                ans=max(ans, dfs(i, nums, memo)+1);
        }
        memo[pos]=ans;
        return ans;
    }
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        vector<int> memo(n);
        int res=0;
        for(int i=0; i<n; i++)
            res=max(res, dfs(i, nums, memo));
        return res;
    }
};

三、反思与改进

这道题我是想用动态规划来做的,但也没做出来,不过这里用不了动态规划,会超时,如果数据量小的话可以使用。贪心 + 二分这种思路没想到,但这种解法才比较具有普遍性,不需要过多考虑数据量的问题(感觉这种题得多做几次才会有思路)。


相关文章
|
1月前
【每日一题Day149】LC2389和有限的最长子序列 | 贪心+前缀和+二分查找
【每日一题Day149】LC2389和有限的最长子序列 | 贪心+前缀和+二分查找
25 0
|
9月前
【动态规划刷题 16】最长等差数列 (有难度) && 等差数列划分 II - 子序列
【动态规划刷题 16】最长等差数列 (有难度) && 等差数列划分 II - 子序列
|
9月前
【动态规划刷题 13】最长递增子序列&& 摆动序列
【动态规划刷题 13】最长递增子序列&& 摆动序列
|
9月前
|
人工智能
【动态规划刷题 11】等差数列划分&& 最长湍流子数组
【动态规划刷题 11】等差数列划分&& 最长湍流子数组
|
1月前
【错题集-编程题】最长公共子序列(一)(动态规划 - LCS)
【错题集-编程题】最长公共子序列(一)(动态规划 - LCS)
|
1月前
【编程题-错题集】最长回文子序列(动态规划 - 区间dp)
【编程题-错题集】最长回文子序列(动态规划 - 区间dp)
|
1月前
|
存储 算法
从动态规划到贪心算法:最长递增子序列问题的方法全解析
从动态规划到贪心算法:最长递增子序列问题的方法全解析
22 1
|
1月前
|
人工智能 算法 Java
最长连续不重复子序列(蓝桥杯每日一题)
最长连续不重复子序列(蓝桥杯每日一题)
25 0
|
7月前
|
算法
代码随想录算法训练营第二天 | LeetCode 977.有序数组的平方、209.长度最小的子数组、59. 螺旋矩阵 II
代码随想录算法训练营第二天 | LeetCode 977.有序数组的平方、209.长度最小的子数组、59. 螺旋矩阵 II
44 0
|
10月前
|
算法 索引
代码随想录算法训练营第二天| 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II ,总结
代码随想录算法训练营第二天| 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II ,总结