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;}
};