《星空解题家》:最长递增子序列LIS—DP(动规)、GA(贪心)、DS(二分搜索)?得解

前言:LIS最长递增子序列问题,是极其有趣的问题,除了最最经典的DP解法外,还有不少解法值得研究
——而,每种解法都能让我们更进一步认识算法的奥妙……
在这里插入图片描述


「解题索引:」

【1】什么是LIS问题《

  1. 递增子序列的定义:对任意一个数组,按原数组下标顺序选择一些元素构成一个新序列,且该序列单调递增
  2. 最长单调子序列:从某数组的所有递增子序列中选择最长的子序列
  3. 我把LIS问题分成两种,(第一种)是只需求最长递增子序列长度的,(第二种)是求出所有最长递增子序列所构成的集合
  4. 最长递增子序列,LIS问题解完后 对(严格单调递增非严格单调递增?)有何区别?【||】将递增改为递减又如何处置——解透LIS问题的本质即可!

【2】最经典的第一类LIS问题》

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

补充:子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。
例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4
解法一:动态规划

补充:
动态规划的关键三点:状态,选择,dp数组/函数——>状态转移
动态规划的核心设计思想:递推(数学归纳)

分析:在这里,我们定义dp[i]表示为从nums[0]到nums[i]间的最长递增子序列长度。(解决子序列问题的经典套路)
根据这个定义,我们可以推出最简的情况(base case)——dp[i]初始值为 1 ,因为以nums[i]结尾的递增子序列至少包含它自己一个元素。
在这里插入图片描述
算法推进的过程是:从i=0开始遍历dp数组,通过dp[0],dp[1]…dp[i-1],来推导dp[i],这里我们发现,dp[i]的状态可以由已知的dp[0…i-1]的状态推知,实现状态转移。现在列出推导过程:
假设dp[0…i-1]已知,很容易知道,只要找到前面结尾比nums[i]小的子序列,然后将nums[i]接到其最后就可以形成一个新的子序列;
即:dp[i]=dp[j]+1,j<i且nums[j]<nums[i];
如果nums[i]前的数均比nums[i]大,那么dp[i]自然就保持初始的 1
不知不觉,我们已经把状态转移方程列出。
以上图为例:
dp[4]=dp[1]+1,nums[1]<nums[4]

具体的代码实现

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
    vector<int>dp(nums.size(),1);//初始化dp数组,均为最简情况1️⃣
    for(int i=0;i<nums.size();i++){
        for(int j=0;j<i;j++){
            if(nums[i]>nums[j])    //从0~i-1,遍历,推导dp[i]
            dp[i]=max(dp[i],dp[j]+1);//dp[i]从中取最大值即可
        }
    }
    int res=0;
    for(int i=0;i<dp.size();i++){
        res=max(res,dp[i]);  //遍历dp[i],从中取最大的dp[i]
    }                       //即为最长递增子序列的长度
    return res;
    }
};

两层嵌套循环,时间复杂度为O(n^2)
解法二:二分搜索

(一开始,我根本想不到这样标准的动态规划题还可以用二分搜索解o()q
思路分析
LIS问题,和一种叫做patience game的纸牌游戏有关,还有种排序算法叫耐心排序
这里直接上代码题解了:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
    vector<int>top(nums.size(),0);
    //初始化牌堆顶
    int piles=0;
    for(int i=0;i<nums.size();i++){
    //要处理的扑克牌
        int poker=nums[i];
        int left=0,right=piles;
        //搜索左边界的二分搜索
        while(left<right){
            int mid=left+(right-left)/2;
            if(top[mid]>poker)right=mid;
            else if(top[mid]<poker)left=mid+1;
            else if(top[mid]==poker)right=mid;
        }
        //没找到合适的纸牌,新建一堆
        if(left==piles)piles++;
        //把该牌放到牌堆顶
        top[left]=poker;
    }
    //牌堆数即为LIS长度
    return piles;
    }
};

时间复杂度为O(n logn)

最长递增子序列问题
前言:这是最最经典的算法题之一,你几乎可以在任何OJ上发现一模一样的LIS问题。
力扣LIS问题
HOJ-LIS问题
POJ-LIS问题
杭电OJ-LIS问题

基本是一模一样的,除了示例…

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0<Solving)1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值