1. 最长递增子序列
我们来看一下我们的贪心策略体现在哪里???
我们来总结一下:
我们在考虑最长递增子序列的长度的时候,其实并不关心这个序列长什么样子,我们只是关心最后一个元素是谁。这样新来一个元素之后, 我们就可以判断是否可以拼接到它的后面。因此,我们可以创建一个数组,统计长度为 x 的递增子序列中,最后一个元素是谁。为了尽可能的让这个序列更长,我们仅需统计长度为x的所有递增序列中最后一个元素的「最小值」。此时我们来算一下时间复杂度,首先我们要遍历整个数组,其次我们还要遍历长度为x的序列,那么此时的复杂度是O(N2),统计的过程中发现,数组中的数呈现「递增」趋势,因此可以使用「二分」来查找插入位置。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> ret;
ret.push_back(nums[0]);
for(int i = 1; i < nums.size(); i++)
{
if(nums[i] > ret.back())
{
ret.push_back(nums[i]);// 如果能接在最后⼀个元素后⾯,直接放
}
else
{
// 使用二分找到插入位置
int left = 0;
int 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. 递增的三元子序列
我们会发现这道题目就是最递增子序列的简化版,因此我们可以使用贪心的思想,找到最长子序列然后判断长度是否大于3即可解决,但是实际上我们不需要使用二分算法,因为我们只需要求出长度为3的子序列,仅需两次比较就可以找到插入位置,同时不用一个数组存数据,仅需两个变量即可,此时的时间复杂度为O(N).
直接来上代码:
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
int a = nums[0];
int b = INT_MAX;
for(int i = 0; i < nums.size(); i++)
{
if(nums[i] > b) return true;
else if(nums[i] > a) b = nums[i];
else a = nums[i];
}
return false;
}
};
3. 最长连续递增序列
这个题目比较简单,找到以某个位置为起点的最长连续递增序列之后(设这个序列的末尾为 j 位置),接下来直接以 j + 1 的位置为起点寻找下⼀个最长连续递增序列,我们没有必要从i + 1位置进行寻找,因为 i 位置找到的序列肯定是最长的!!!
class Solution {
public:
int findLengthOfLCIS(vector<int>& nums) {
int ret = 0;
int i = 0;
while(i < nums.size())
{
int j = i + 1;
// 找到递增区间的末端
while(j < nums.size() && nums[j] > nums[j-1])
{
j++;
}
ret = max(ret,j - i);
// 直接在循环中更新下⼀个位置的起点
i = j; // 贪心
}
return ret;
}
};
4. 买卖股票的最佳时机
首先我们看到这道题目,第一想到的肯定是暴力枚举,我们可以依次枚举两个位置,然后进行相减,最后保存相减出来的最大值即可,但是这样的复杂度就是O(N2)的,此时我们就可以进行优化,我们在枚举卖出价格时候,并不用将前面买入的股票的价格依次枚举,我们只需要找到其中的最小值即可,这一个点就体现出来贪心的策略,由于只能交易⼀次,所以对于某⼀个位置 i ,要想获得最大利润,仅需知道前⾯所有元素的最小值。然后在最小值的位置「买入」股票,在当前位置「卖出」股票即可。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int prevmin = INT_MAX;
int ret = 0; // 记录最终结果
for(int i = 0; i < prices.size(); i++)
{
// 先更新结果
ret = max(prices[i] - prevmin, ret);
// 再更新最小值
if(prices[i] < prevmin)
prevmin = prices[i];
}
return ret;
}
};
5. 买卖股票的最佳时机Ⅱ
由于可以进行⽆限次交易,所以只要是⼀个「上升区域」,上升区间一定是稳赚的,我们就把利润拿到手就好了,这就是贪心策略。
⭐解法一:双指针
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ret = 0;
for(int i =0 ; i < prices.size(); )
{
int j = i;
while(j + 1 < prices.size() && prices[j + 1] > prices[j])
{
j++; // 寻找上升的区间
}
ret += prices[j] - prices[i];
i = j + 1;
}
return ret;
}
};
⭐解法二:拆分交易,只要今天的股票的价格大于昨天,就可以累计到利润上
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ret = 0;
for(int i = 1; i < prices.size(); i++)
{
if(prices[i] > prices[i - 1])
ret += prices[i] - prices[i - 1];
}
return ret;
}
};