贪心算法篇——万千抉择中的唯一考量,最优解追寻的跬步累积(3)


在这里插入图片描述

引言

初篇我们介绍了贪心算法的相关背景知识,本篇我们将结合具体题目,进一步深化大家对于贪心算法的理解和运用。

一、最长递增子序列

1.1 题目链接:https://leetcode.cn/problems/longest-increasing-subsequence/description/

1.2 题目分析:

  • 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
  • 子序列 是由子数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。
  • 例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
  • 也就是说,子序列不要求严格连续

1.3 思路讲解:

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

因此,我们可以创建⼀个数组,统计⻓度为 x 的递增⼦序列中,最后⼀个元素是谁。为了尽可能
的让这个序列更⻓,我们仅需统计⻓度为 x 的所有递增序列中最后⼀个元素的「最⼩值」。
统计的过程中发现,数组中的数呈现「递增」趋势,因此可以使⽤「⼆分」来查找插⼊位置。

1.4 代码实现:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        vector<int> ret;
        ret.push_back(nums[0]);
        for(int i=1;i<n;i++)
        {
            if(nums[i]>ret.back())
            {
                ret.push_back(nums[i]);
            }//满足递增条件,直接插入
            else
            //二分查找插入位置
            {
                int left=0,right=ret.size()-1;
                while(left<right)
                {
                int mid=(left+right)/2;
                if(ret[mid]<nums[i])
                {
                    left=mid+1;
                }
                else
                {
                    right=mid;
                }
                }
                ret[left]=nums[i];//放在left位置上
                

            }
        }
        return ret.size();
    }
};

二、递增的三元子序列

2.1 题目链接:https://leetcode.cn/problems/increasing-triplet-subsequence/description/

2.2 题目分析:

  • 给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。
  • 如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。

2.3 思路讲解:

最⻓递增⼦序列的简化版
不⽤⼀个数组存数据,仅需两个变量即可。也不⽤⼆分插⼊位置,仅需两次⽐较就可以找到插⼊位
置。

  • 首先将a初始化为nums[0],b初始化为INT_MAX,之后逐个遍历数组
  • 如果nums[i]>b,说明一定存在,直接返回即可
  • 如果nums[i]>a,令b=nums[i],保持a->b的递增关系
  • 如果nums[i]<a,令a=nums[i],贪心策略(更小的值充当递增序列的最小值,更容易匹配到可能存在的递增序列)

2.4 代码实现:

class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        int a=nums[0],b=INT_MAX;
        for(int i=1;i<nums.size();i++)
        {
            if(nums[i]>b) return true;
            else if(nums[i]>a) b=nums[i];//更新b的值
            else if(nums[i]<a) a=nums[i];//更新a的值
        }
        return false;//说明不存在
        
    }
};

三、最长连续递增子序列

3.1 题目链接:https://leetcode.cn/problems/longest-continuous-increasing-subsequence/description/

3.2 题目分析:

  • 给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
  • 该子序列内,元素必须是相邻的

3.3 思路讲解:

本题可以通过双指针进行贪心优化

贪⼼策略:
找到以某个位置为起点的最⻓连续递增序列之后(设这个序列的末尾为 j 位置),接下来直接
以 j + 1 的位置为起点寻找下⼀个最⻓连续递增序列。

3.4 代码实现:

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int n=nums.size();
        int ret=0;//最终返回长度
        for(int i=0;i<n;)
        {
            int j=i+1;
            while(j<n && nums[j]>nums[j-1]) //处于连续递增之中
            {
                j++;
            }
            ret=max(ret,j-i);//更新最大长度
            i=j;//更新起点
        }
        return ret;
        
    }
};

四、买卖股票的最佳时机

4.1 题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/

4.2 题目分析:

  • 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
  • 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。
  • 返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

4.3 思路讲解:

需要注意:本题中只能交易一次(即只能买入和卖出各一次)

贪心策略:

  • 由于只能交易⼀次,所以对于某⼀个位置 i ,要想获得最⼤利润,仅需知道前⾯所有元素的最⼩
    值。然后在最⼩值的位置「买⼊」股票,在当前位置「卖出」股票即可。
  • 记录在不同位置卖出时的最大值,最后直接返回该最大利润即可

4.4 代码实现:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ret=0;//处理不能获取利润的情况
        int prev=INT_MAX;//前缀最小值
        for(int i=0;i<prices.size();i++)
        {
            ret=max(ret,prices[i]-prev);
            prev=min(prev,prices[i]);
        }
        return ret;
    }
};

小结

本篇关于贪心算法的介绍就暂告段落啦,希望能对大家的学习产生帮助,欢迎各位佬前来支持斧正!!!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值