
引言
初篇我们介绍了贪心算法的相关背景知识,本篇我们将结合具体题目,进一步深化大家对于贪心算法的理解和运用。
一、最长递增子序列
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;
}
};
小结
本篇关于贪心算法的介绍就暂告段落啦,希望能对大家的学习产生帮助,欢迎各位佬前来支持斧正!!!