一文解决Leetcode买卖股票问题

对于前3个问题,均采用了比较巧妙的解法。由于第4个问题具有非常强的泛型,因此采用了DP,第4个问题的dp如果理解的话,实际上只需要稍加修改状态便可以用该dp思路应用于所有的6个题中。在后两个问题便是基于该思路用dp解决的。

文章目录

  • 一文解决Leetcode买卖股票问题
    • [121. 买卖股票的最佳时机](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/)
    • [122. 买卖股票的最佳时机 II](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
    • [123. 买卖股票的最佳时机 III](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/)
    • [188. 买卖股票的最佳时机 IV](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/)
    • [714. 买卖股票的最佳时机含手续费](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/)
    • [309. 最佳买卖股票时机含冷冻期](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/)

121. 买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

分析:对于每一天而言,如果当天卖股票,则当天能够获得的最大利润就是当天的股票价格减去之前的最小值,也就是历史最低值。 故动态记录历史最低值和最大利润即可,遍历一遍数组。

class Solution {public:int maxProfit(vector<int>& prices) {if(prices.size()==0||prices.size()==1) return 0;int min_price=prices[0]; //记录之前的最小值,也就是历史最低点int max_profit=0; //记录最大利润for(int i=0;i<prices.size();i++){max_profit=max(prices[i]-min_price,max_profit);if(prices[i]<min_price) //出现新的最低点min_price=prices[i];}return max_profit;}
};

122. 买卖股票的最佳时机 II

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

分析:将股票价格用折线图的方式呈现出来:


怎样获得尽可能大的利润?实际上,对于这个折线图来说,每次上升的坡度都是可以获得利润的,如果每次在上升曲线的的最低点买入,在最高点卖出,便可以获得最大利润。因此,每次记录当前的波谷,然后找到递增的最高处即可,之后反复。但是这样实现起来还是稍微麻烦一些,由于可以当天卖出当天买入,因此,只要某天价格低于下一天价格,就在这天买入下一天卖出。(注意虽然下一天卖出了,但是如果第三天价格高于第二天,仍然会第二天再买入第三天卖出) 。也就是将能获取的一切利润都获取了 总体思路和最低买入最高卖出一样的。

class Solution {public:int maxProfit(vector<int>& prices) {int profit=0;for(int i=0;i<prices.size()-1;i++){if(prices[i]<prices[i+1]) //第二天高于今天价格profit+=prices[i+1]-prices[i];}return profit;}
};

123. 买卖股票的最佳时机 III

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:
输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。

分析:划分法,将两次交易划分开

如果进行两次交易,则一定是在第i天前进行一次交易,在第i天后进行一次交易,因此,按照该思路,仿照只能进行一次交易时的计算策略,将对于所有的i,第i天前进行交易的最大值计算出,对于所有的j,将第j天后进行交易的最大值计算出,然后遍历,找到最优的i使得两次交易的利润和最大。

  • 正向遍历时,是已知前面的最小值,因此要让利润最大就是尽可能找最大值

    f i r s t [ i ] = m a x ( f i r s t [ i − 1 ] , p r i c e s [ i ] − m i n _ p r i c e ) first[i]=max(first[i-1],prices[i]-min\_price) first[i]=max(first[i−1],prices[i]−min_price)

  • 反向遍历时,是已知后面的最大值,因此要让利润最大就是尽可能找最小值

    l a s t [ i ] = m a x ( l a s t [ i + 1 ] , m a x _ p r i c e − p r i c e s [ i ] ) last[i]=max(last[i+1],max\_price-prices[i]) last[i]=max(last[i+1],max_price−prices[i])

  • 时间复杂度为O(n),空间复杂度为O(n)

class Solution {public:int maxProfit(vector<int>& prices) {if(prices.size()==0||prices.size()==1) return 0;vector<int> first(prices.size());first[0]=0;vector<int> last(prices.size());last[prices.size()-1]=0;//求first[]时正着求,已经最小值,找后面尽可能大的值int min_price=prices[0];for(int i=1;i<prices.size();i++){first[i]=max(first[i-1],prices[i]-min_price);min_price=min(min_price,prices[i]);}//求last[]时候要倒着求,是已知最大值,找前面尽可能小的值int max_price=prices[prices.size()-1];for(int i=prices.size()-2;i>=0;i--){last[i]=max(last[i+1],max_price-prices[i]);max_price=max(max_price,prices[i]);}int max_profit=0;for(int i=0;i<prices.size();i++)max_profit=max(first[i]+last[i],max_profit);return max_profit;}
};

188. 买卖股票的最佳时机 IV

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:
输入: [2,4,1], k = 2
输出: 2
解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。示例 2:
输入: [3,2,6,5,0,3], k = 2
输出: 7
解释: 在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。

分析:显然,这个问题,是前面三个问题的高度泛化。处理前面三个问题时,用到了很多技巧与规律,但显然,处理该问题必须得有一个比较泛化的思路。(该题的解法很明显可以应用于之前三个题)

DP动态规划法

每天的状态由:是否持有股票,已经进行了几次交易即可表示。

因此,令dp[天数] [是否持有股票] [进行的交易次数] 来表示在第i天已经进行了k次交易的情况下现在持有或者不持有股票的情况下的利润。因此在每天结束时,会有多种状态,且每天状态受前一天影响。具体为:

需要补充的是,如果直接根据 m a x _ k max\_k max_k 就设立数组,那当给定 m a x _ k max\_k max_k是一个非常大的数字时,将会导致空间的溢出,且进行了太多没有必要的计算,实际上当 m a x _ k max\_k max_k 大于天数的一半时,已经说明交易次数是没有限制的,故此时采用贪心法求解将更加节省时间和空间

根据这些状态完成代码:

class Solution {public:int maxProfit(int max_k, vector<int>& prices) {if(prices.size()==0||prices.size()==1) return 0;if(max_k>prices.size()/2) return maxProfit2(prices);//相当于无限交易int length=prices.size();int dp[length][2][max_k+1];//进行部分初始化,主要将第一天的情况进行初始化dp[0][1][0]=-prices[0];  //第一天买入了股票dp[0][0][0]=0;for(int i=1;i<length;i++)  {dp[i][0][0]=0;//没有进行过交易且未持股dp[i][1][0]=max(dp[i-1][1][0],-prices[i]);//没有进行过交易且持股}for(int k=1;k<=max_k;k++){ //第一天不存在卖出的情况dp[0][1][k]=INT_MIN/2;dp[0][0][k]=INT_MIN/2;}for(int i=1;i<length;i++)for(int k=1;k<=max_k;k++){//对所有情况用状态转移方程求解dp[i][0][k]=max(dp[i-1][1][k-1]+prices[i],dp[i-1][0][k]);dp[i][1][k]=max(dp[i-1][0][k]-prices[i],dp[i-1][1][k]);}int result=0;for(int k=0;k<=max_k;k++)result=max(dp[length-1][0][k],result);return result;}int maxProfit2(vector<int>& prices) //贪心求解无限制k问题{int profit=0;for(int i=0;i<prices.size()-1;i++){if(prices[i]<prices[i+1]) //第二天高于今天价格profit+=prices[i+1]-prices[i];}return profit;}
};

714. 买卖股票的最佳时机含手续费

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

示例 1:
输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
输出: 8
解释: 能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.

分析:在之前买卖股票的基础上,增加了手续费这一概念,当然可以在问题2的方法上,每次取上升曲线的最低点买入最高点卖出,如果收益大于手续费则选择进行这一操作,否则跳过这一曲线,思路简单,但实现起来稍微繁琐一些,既然上一个问题已经采用了DP,在此仍采用DP思路。

每天的状态由:持有股票,未持有股票 组成,因为交易次数不受限制,故可以忽略k的影响。用 d p [ 天 数 ] [ 状 态 ] dp[天数] [状态] dp[天数][状态]表示,在此处我将手续费记录在买入股票时候

//最初的dp思路
class Solution {public:int maxProfit(vector<int>& prices, int fee) {if(prices.size()==0||prices.size()==1)return 0;int length=prices.size();int dp[length][2];dp[0][0]=0;dp[0][1]=-prices[0]-fee;for(int i=1;i<length;i++){dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]-fee);}return dp[length-1][0];}
};
//优化空间的dp思路
class Solution {public:int maxProfit(vector<int>& prices, int fee) {if(prices.size()==0||prices.size()==1)return 0;int length=prices.size();int pre_0=0,pre_1=-prices[0]-fee; //表示昨天的未持股和持股int ex_0=pre_0,ex_1=pre_1; //表示当天的未持股和持股for(int i=1;i<length;i++){ex_0=max(pre_0,pre_1+prices[i]);ex_1=max(pre_1,pre_0-prices[i]-fee);pre_0=ex_0;pre_1=ex_1;}return ex_0;}
};

309. 最佳买卖股票时机含冷冻期

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

分析:该题是在之前第二个问题基础上增加了冷冻期,增加了冷冻期将使得最初的想法实现时有些问题,因此也采用dp的方法进行状态转移。

实际上,每天的状态还是两种,持有股票和未持有股票,这里发生改变的,是如果想要持有股票,要么是昨天就持有股票并且没有卖,要么是前天没有持有股票(即至少前天已经卖出) 用 d p [ 天 数 ] [ 状 态 ] dp[天数][状态] dp[天数][状态] 表示

class Solution {public:int maxProfit(vector<int>& prices) {if(prices.size()==0||prices.size()==1)return 0;int length=prices.size();int dp[length][2];//初始化dp[0][0]=0;dp[0][1]=-prices[0];dp[1][0]=max(dp[0][0],dp[0][1]+prices[1]);dp[1][1]=max(dp[0][1],-prices[1]);for(int i=2;i<length;i++){dp[i][0]=max(dp[i-1][1]+prices[i],dp[i-1][0]);dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i]);}return dp[length-1][0];}
};

leetcode买卖股票问题(思路、方法、code)相关推荐

  1. LeetCode买卖股票之一:基本套路(122)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于<LeetCode买卖股票>系列 在L ...

  2. LeetCode买卖股票的最佳时机系列总结

    LeetCode买卖股票的最佳时机系列总结 此类动态规划从二维动规理解后优化到一维动规,部分题目还可以用到贪心. 目录: 121 买卖股票的最佳时机1 122 买卖股票的最佳时机2 123 买卖股票的 ...

  3. leetcode 买卖股票问题

    leetcode 买卖股票问题 lc121 买卖股票最佳时机 lc122 买卖股票最佳时机II lc123. 买卖股票的最佳时机 III lc188. 买卖股票的最佳时机 IV lc121 买卖股票最 ...

  4. leetcode 买卖股票系列题目总结

    总结:买卖股票系列题目 1.买卖股票的最佳时机(简单) 121. 买卖股票的最佳时机 难度简单1093 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 如果你最多只允许完成一笔交易( ...

  5. LeetCode 买卖股票的最佳时机 - 超详细讲解系列题

    1.分析 使用通用方法,也即动态规划DP (1)LeetCode 121. 买卖股票的最佳时机 class Solution {public int maxProfit(int[] prices) { ...

  6. LeetCode 买卖股票的最佳时机 6道题1个解法总结

    一个方法解决6道买卖股票题:来自LeetCode题解 一.穷举框架 利用「状态」进行穷举.我们具体到每一天,看看总共有几种可能的「状态」,再找出每个「状态」对应的「选择」.我们要穷举所有「状态」,穷举 ...

  7. [Leetcode] 买卖股票合集(动态规划)

    写完这套题,再搞一台时光机,财务自由不是梦(Doge) ================================== 相关题目链接 121 买卖股票的最佳时机 122 买卖股票的最佳时机 II ...

  8. LeetCode买卖股票问题集合(六种股票问题)

    一.买卖股票的最佳时机 (LeetCode 121) 简单的动态规划问题,只能完成一次买卖,定义二维 dp[i] [] 数组,dp [i] [0]表示没有股票的状态,dp [i] [1]表示持有股票的 ...

  9. 【leetcode买卖股票系列问题】多次买卖/手续费/冻结期

    一.目录 一.目录 二.股票系列问题 1.买卖股票的最佳时机(121题) 1.1.题目 1.2.思路 1.3.代码实现(1种) 2.买卖股票的最佳时机II(122题) 2.1.题目 2.2.思路 2. ...

最新文章

  1. git常用命令及规范流程
  2. CSS实现超过一定的宽度添加省略
  3. 在元宇宙里怎么交朋友?Meta发布跨语种交流语音模型,支持128种语言无障碍对话...
  4. JS学习之路之JavaScript match() 方法
  5. springboot获取payload_Spring Boot 使用 JSR303 实现参数验证
  6. 如何判断derived-to-base conversion是否legal
  7. MongoDB 4.2 内核解析 - Change Stream
  8. 双极结型三极管及放大电路基础
  9. Flask 上下文管理-- (session,request,current_app的传递)--类似本地线程实现,以及多app应用...
  10. python算法常用技巧与内置库
  11. 如何把windowsXP系统主题成Windows7风格windowsxp主题包
  12. 讯飞输入法pad版x86_讯飞输入法Pad版x86版
  13. 推荐一款限时下载应用(听心字典)
  14. 高级JAVA面试题详解(三)——Redis(redis cluster、虚拟槽、一致性hash算法、master选举、淘汰策略、String数据结构)
  15. 使用C语言构造一个简单计算器
  16. YouTube上的版权保护
  17. 如何用Python写一个小游戏(2)
  18. 来自一枚初生牛犊不怕虎的小菜鸟的Mock.js使用,不足之处欢迎读者的指出 谢谢...
  19. 数据结构初步(十二)- 插入排序与希尔排序超详细图解分析
  20. cent怎么读(centre怎么读)

热门文章

  1. 「C++控制台生存游戏」暗黑体素 DarkVoxel 控制台版
  2. 计划处理链的很多种情况factory calendar
  3. python环境-基于go-cqhttp-简易qq聊天机器人
  4. 一篇文章看懂三种存储方式DAS、NAS、SAN
  5. cesium实现动态扩散墙效果(基于turf)
  6. [python] say hi
  7. 宜信 、《麻省理工科技评论》共同揭晓: 35岁以前最有可能改变世界的中国科技精英丨Xtecher 前线
  8. 【01】计算机的发展
  9. Spring基本用法1——Spring的核心机制:IOC、DI
  10. 箱线图怎么看_股票000882:怎么看月k线图「歙县股票网」