leetcode 300. Longest Increasing Subsequence (最长递增序列也叫最长上升子序列)

题目描述
给出一个未排序的整数数组,找出最长递增子序列的长度。

样例
输入: [10,9,2,5,3,7,101,18]
输出:4
说明:最长递增子序列为[2,3,7,101],长度为4,可能有多个可能的最长递增子序列,此题只需要返回长度即可

算法1
(动态规划)O(n^2)
用数组dp[i]记录以nums[i]结尾(即nums[i]为最后一个数字)的最长递增子序列的长度,则递推方程为 dp[i]=max(dp[j]+1),其中要求1≤j<i且nums[j]<nums[i]nums[j]<nums[i]。

时间复杂度分析:对每个i(1≤i≤ni,都需要从1遍历到i,则时间复杂度为O(n^2),空间复杂度的话需要一个额外的dp数组,空间复杂度为O(n^2)。

代码如下:

 int lengthOfLIS(vector<int>& nums) {
        if(nums.size()==0)
            return 0;
         vector<int> dp(nums.size(),1);//这个之所以赋值为1,是因为下面dp[i]=max(dp[i],dp[j]+1)这个在计算的时候如   果     //dp[i]=max(dp[i],dp[j]+1)的时候,dp[i]的第二个实际上是2,但是如果全部初始化为0.则dp[i]=max(0,0+1)=1,但实际上是2,
        int res = 1;

//维持递增序列的就是j在i之后,然后用if(nums[i]>nums[j]),还有这个是序列,所以是两个for循环的,一个字符一个字符比较
        for(int i= 1;i<nums.size();i++){
            for(int j = 0;j<i;j++){
                if(nums[i]>nums[j])
                    dp[i] = max(dp[i],dp[j]+1);
            }
            if(dp[i]>res)
                res = dp[i];
        }
        return res;

}

也可以看下面的:

 int lengthOfLIS(vector<int>& nums) {
        if(nums.size()==0)
            return 0;
         vector<int> dp(nums.size(),0);//这个之所以赋值为1,是因为下面dp[i]=max(dp[i],dp[j]+1)这个在计算的时候如   果     //dp[i]=max(dp[i],dp[j]+1)的时候,dp[i]的第二个实际上是2,但是如果全部初始化为0.则dp[i]=max(0,0+1)=1,但实际上是2,
        int res = 1;
        for(int i= 1;i<nums.size();i++){
            for(int j = 0;j<i;j++){
                if(nums[i]>nums[j])
                    dp[i] = max(dp[i],dp[j]+1);
            }
            if(dp[i]+1>res)
                res = dp[i]+1;
        }
        return res;

}

方法2:动态规划+二分(时间复杂度是O(logN) ,空间复杂度是O(1))

说实话,正常人基本想不到这种解法(也许玩过某些纸牌游戏的人可以想出来)。所以如果大家了解一下就好,正常情况下能够给出动态规划解法就已经很不错了。

为了简单期间,后文跳过所有数学证明,通过一个简化的例子来理解一下思路。

首先,给你一排扑克牌,我们像遍历数组那样从左到右一张一张处理这些扑克牌,最终要把这些牌分成若干堆。

最长递增子序列和一种叫做 patience game 的纸牌游戏有关,甚至有一种排序方法就叫做 patience sorting(耐心排序)。

为了简单期间,后文跳过所有数学证明,通过一个简化的例子来理解一下思路。

首先,给你一排扑克牌,我们像遍历数组那样从左到右一张一张处理这些扑克牌,最终要把这些牌分成若干堆。

处理这些扑克牌要遵循以下规则:

只能把点数小的牌压到点数比它大的牌上。如果当前牌点数较大没有可以放置的堆,则新建一个堆,把这张牌放进去。如果当前牌有多个堆可供选择,则选择最左边的堆放置。

比如说上述的扑克牌最终会被分成这样 5 堆(我们认为 A 的值是最大的,而不是 1)。

为什么遇到多个可选择堆的时候要放到最左边的堆上呢?因为这样可以保证牌堆顶的牌有序(2, 4, 7, 8, Q),证明略。

按照上述规则执行,可以算出最长递增子序列,牌的堆数就是最长递增子序列的长度,证明略。

我们只要把处理扑克牌的过程编程写出来即可。每次处理一张扑克牌不是要找一个合适的牌堆顶来放吗,牌堆顶的牌不是有序吗(即2 4 7 8 Q),这就能用到二分查找了:用二分查找来搜索当前牌应放置的位置。

PS:旧文二分查找算法详解详细介绍了二分查找的细节及变体,这里就完美应用上了。如果没读过强烈建议阅读。

作者:labuladong
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-she-ji-fang-fa-zhi-pai-you-xi-jia/

class Solution {

public:

    int lengthOfLIS(vector<int>& nums) {

//int v[nums.size()]={0};

        vector<int>v(nums.size(), 0);

        if (nums.size() == 0) return 0;

        // 牌堆数初始化为 0

 

        int piles = 0;

        for (int i = 0; i<nums.size(); i++)

        {

            // 要处理的扑克牌,即将这个扑克牌插到哪里

            int poker = nums[i];

            /***** 搜索左侧边界的二分查找 *****/

            int left = 0, right = piles;//right是堆数

            while (left<right)

            {

                int mid = (left + right) / 2;

                if (v[mid] >= poker)//如果中间位置的数是大于要插入的扑克

                {

                    right = mid;

                }

                else

                {

                    left = mid + 1;

                }

            

            }

                //没找到合适的牌堆,新建一堆

                if (left == piles)piles++;

                v[left] = poker;

        }

        return piles;

    }

};

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值