贪心算法 -- 递增子序列

目录

最长递增子序列

题解:

代码:

递增的三元子序列

题解:

代码:

简易版:

最长连续递增序列

题解:

代码: 


最长递增子序列

300. 最长递增子序列 - 力扣(LeetCode)https://leetcode.cn/problems/longest-increasing-subsequence/description/

题解:

如果我们要使上升子序列尽可能的长,则我们需要让序列上升得尽可能慢,因此我们希望每次在上升子序列最后加上的那个数尽可能的小

以数组 nums = [ 7, 3, 8, 4, 7, 2, 14, 13 ] 为例,从下标为 0 位置开始遍历:

当访问 nums[ 0 ] 时,此时长度为 1 的子序列的元素为 7

当访问 nums[ 1 ] 时,由于 3 < 7,所以 7 和 3 无法构成递增子序列,所以长度为 1 的子序列的元素为 3 或者 7,此时长度为 1 的子序列的元素应该保留 3,去掉 7,原因如下:假如再来一个元素 x,

  • 如果 x > 7,此时长度为 2 的子序列有两种可能,[ 3, x ] 或者 [ 7, x ] ;
  • 如果 3 < x <= 7,那么长度为 2 的子序列只有 [ 3, x ] 一种情况。

也就是说,能与 7 构成递增子序列的元素,也一定可以与 3 构成递增子序列,但能与 3 构成递增子序列的元素不一定可以与 7 构成递增子序列,所以保留 3 去掉 7 可以让递增子序列的长度尽可能长!此时长度为 1 的子序列的最后一个元素为 3.

当访问 nums[ 2 ] 时,8 可以和 3 构成递增子序列,此时长度为 1 的子序列的最后一个元素为 3,长度为 2 的子序列的最后一个元素为 8.

当访问 nums[ 3 ] 时,4 比 8 小,而且 4 比长度为 1 的子序列的最后一个元素 3 大,此时长度为 2 的子序列有两种情况,[ 3, 4 ] 或者 [ 3, 8 ] ,和上面同理,能与 [ 3, 8 ] 构成递增子序列的元素,也可以与 [ 3, 4 ] 构成递增子序列,但能与 [ 3, 4 ] 构成递增子序列的元素,不一定能与 [ 3, 8 ] 构成递增子序列。所以长度为 2 的子序列的最后一个元素更新为 4.

 当访问 nums[ 4 ] 时,7 比 4 大,可以与 [ 3, 4 ] 构成递增子序列,此时长度为 3 的子序列的最后一个元素为 7

当访问 nums[ 5 ] 时,2 比 3 小,和上面同理,长度为 1 的子序列的最后一个元素更新为 2.

当访问 nums[ 6 ] 时,14 比 7 大,可以构成递增子序列,此时长度为 4 的子序列的最后一个元素为 14

当访问 nums[ 7 ] 时,13 比 14 小,但比 7 大,此时长度为 4 的子序列的最后一个元素为 14

 

从上述过程可以看出,假设用数组 ret 来保存长度为 x 的子序列的最后一个元素,当前数组的长度为 n:

  • 当前遍历到的元素 a 比 当前长度最长的子序列的最后一个元素时, 即 a 大于数组的最后一个元素,则递增子序列的长度+1,即数组 ret 的长度 n++,且 ret [ n ] =  a;
  • 当前遍历到的元素 a 比 当前长度最长的子序列的最后一个元素时,则需要在数组中找到大于等于 a 的数中的最小值,然后用 a 把这个数覆盖掉。

思想就是让 ret 中存储比较小的元素。这样,ret 未必是真实的最长上升子序列,但长度是对的,只能说明我们曾经得到过这一长度的递增子序列

如何快速在数组中找到大于等于 a 的数中的最小值?

用二分查找思想,根据数组元素的更新策略,可以看出数组中的元素是递增的,假设数组中第一个大于等于 a 的数的下标为 i,数组的长度为 n,那么数组可以被划分为两部分,下标范围为 [ 0, i-1 ] 的数都比 a 小,下标范围为 [ i , n-1 ] 的数都大于等于 a,此二段性可以帮助我们快速定位要把元素 a 放在哪里。 

代码:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
         vector<int> ret;
        ret.push_back(nums[0]);
        int n=nums.size();

        for(int i=1;i<n;i++)
        {
            int x=nums[i];
            if(x>ret.back())
            {
                ret.push_back(x);
            }
            else
            {
                int left=0,right=ret.size()-1;
                while(left<right)
                {
                    int mid=left+(right-left)/2;
                    if(ret[mid]<x)  left=mid+1;
                    else    right=mid;
                }
                ret[left]=x; 
            }
        }
        return ret.size();   
    }
};

递增的三元子序列

334. 递增的三元子序列 - 力扣(LeetCode)https://leetcode.cn/problems/increasing-triplet-subsequence/description/

题解:

和上一题同理,如果 ret 数组的长度为 3,说明存在递增的三元子序列,此时直接返回 true 即可。

代码:

class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        vector<int> ret;
        ret.push_back(nums[0]);
        int n=nums.size();
        for(int i=1;i<n;i++)
        {
            if(nums[i]>ret.back())
            {
                ret.push_back(nums[i]);
                if(ret.size()==3)
                    return true;
            }
            else
            {
                int left=0,right=ret.size()-1;
                while(left<right)
                {
                    int mid=left+(right-left)/2;
                    if(ret[mid]<nums[i])    left=mid+1;
                    else right=mid;
                }
                ret[left]=nums[i];
            }
        }
        return false;
    }
};

简易版:

class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        int first=nums[0],second=INT_MAX;
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]>second) return true;
            else if(nums[i]>first)  second=nums[i];
            else first=nums[i];
        }
        return false;
    }
};

最长连续递增序列

674. 最长连续递增序列 - 力扣(LeetCode)https://leetcode.cn/problems/longest-continuous-increasing-subsequence/description/

题解:

本题其实是一个滑动窗口。注意窗口不要越界,且窗口左闭右开。left 是窗口的左端点,right 是窗口的右端点。

代码: 

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int left=0,right=0,n=nums.size(),ret=0;
        while(left<n)
        {
            right=left+1;
            while(right<n && nums[right]>nums[right-1])
                right++;
            
            ret=max(ret,right-left);//左闭右开
            left=right;
        }
        return ret;
    }
};

上一篇:2024-11-18-sklearn学习(1)-线性回归(2)


下一篇:i春秋-签到题