Leetocode全部5道买卖股票问题总结(121+122+123+188+309)

Posted fdwzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetocode全部5道买卖股票问题总结(121+122+123+188+309)相关的知识,希望对你有一定的参考价值。

题目1----121. 买卖股票的最佳时机I:

链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

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

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

解答:

限制只能买入卖出一次

DP1:

buy[i]记录截止到第i天是买入的状态的最小花费(值为负数)

 1 class Solution {
 2 public:
 3     int maxProfit(vector<int>& prices) {
 4         if(prices.size()<2){
 5             return 0;
 6         }
 7         int n=prices.size(),res=0;
 8         vector<int> buy(n,0);
 9         buy[0]=-prices[0];
10         for(int i=1;i<n;++i){
11             res=max(buy[i-1]+prices[i],res);
12             buy[i]=max(-prices[i],buy[i-1]);
13         }
14         return res;
15     }
16 };

严格来说这个不算dp,计算第i天的情况时,只用到了buy[i-1]的数据。。所以前面保存的数据是没有意义的。

DP2:

dp[i]记录第i天当天卖出的最大利润,则最大利润一定等于之前某天买今天卖。

i):首先可以昨天买今天卖。

ii):还可以之前某天买今天卖。dp[i-1]等于昨天之前买入,i-1天卖出的最大利润,那么i-1天不卖,改为第i天卖也可以得到一个利润。

两个利润取最大。

 1 class Solution {
 2 public:
 3     int maxProfit(vector<int>& prices) {
 4         if(prices.size()<2){
 5             return 0;
 6         }
 7         int n=prices.size();
 8         vector<int> dp(n,0);
 9         dp[0]=0;
10         int res=0;
11         for(int i=1;i<n;++i){
12             dp[i]=max(0,prices[i]-prices[i-1]);
13             dp[i]=max(dp[i],dp[i-1]-prices[i-1]+prices[i]);
14             res=max(res,dp[i]);
15         }
16         return res;
17     }
18 };

 

遍历:

和DP一样都是O(N)时间,和第一种dp一样想法,只是不用记录dp数组了。毕竟考察i的时候,只需要buy[i-1]的数据。
思路:最高利润出现在:买入为价格最低时,卖出为买入之后价格最高时。
故当更新最小值价格时,之前的最大价格要舍弃,从当前索引继续考察。

 1 class Solution:
 2     def maxProfit(self, prices: List[int]) -> int:
 3         l=len(prices)
 4         if l<2:
 5             return 0
 6         _min,_max=0,0
 7         i=0
 8         res=0
 9         while i<l:
10             if prices[i]<prices[_min]:
11                 _min=i
12                 _max=i
13             if prices[i]>prices[_max]:
14                 _max=i
15             res=max(res,prices[_max]-prices[_min])
16             i+=1
17         return res

 

题目2----122. 买卖股票的最佳时机II:

链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

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

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

解答:

无限制次数的买入卖出

 1 class Solution {
 2 public:
 3     int maxProfit(vector<int>& prices) {
 4         int n=prices.size();
 5         if(n<2){return 0;}
 6         vector<int> buy(n,0),sell(n,0);
 7         buy[0]=-prices[0];
 8         for(int i=1;i<n;++i){
 9             sell[i]=max(sell[i-1],prices[i]+buy[i-1]);//之前卖今天不卖 or 之前买今天卖
10             buy[i]=max(buy[i-1],sell[i-1]-prices[i]);//之前买今天不买 or 之前卖了今天买
11         }
12         return sell[n-1];
13     }
14 };

 

题目3----123. 买卖股票的最佳时机III:

链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/

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

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

解答:

限制最多两次买入卖出

定义了4个dp数组,分别是

buy1[i]:第i天是第一次买入的状态

sell[i]:第i天是第一次卖出的状态

buy2、sell2同理。

由于一开始还不能第二次买和第二次卖,所以赋值为负无穷。

 1 class Solution {
 2 public:
 3     #define inf INT_MIN
 4     int maxProfit(vector<int>& prices) {
 5        int n=prices.size();
 6        if(n<2){return 0;}
 7        vector<int> buy1(n,0),sell1(n,0),buy2(n,inf),sell2(n,inf);
 8        buy1[0]=-prices[0];
 9        for(int i=1;i<n;++i){
11            sell1[i]=max(prices[i]+buy1[i-1],sell1[i-1]);
12            buy1[i]=max(-prices[i],buy1[i-1]);
13            sell2[i]=max(buy2[i-1]+prices[i],sell2[i-1]);
14            buy2[i]=max(buy2[i-1],sell1[i-1]-prices[i]);
16        }
17        return max(0,max(sell2[n-1],sell1[n-1]));
18     }
19 };

方法2:先计算1次买卖最大的利润sell1[i]。再计算从第i天开始再买卖一次最大的利润sell2[i]。

第一次买卖是从前向后求(因为左侧是固定的),第二次买卖是从后向前求(因为右侧是固定的)

 1 class Solution {
 2 public:
 3     #define inf INT_MIN
 4     int maxProfit(vector<int>& prices) {
 5        int n=prices.size();
 6        if(n<2){return 0;}
 7        vector<int> sell1(n,0),sell2(n,0);
 8        int min_buy=-prices[0];
 9        for(int i=1;i<n;++i){
10            sell1[i]=max(sell1[i-1],prices[i]+min_buy);
11            min_buy=max(min_buy,-prices[i]);
12        }
13        int res=max(0,sell1[n-1]);
14        int max_sell=prices[n-1];
15        for(int i=n-2;i>=0;--i){
16            sell2[i]=max(sell2[i+1],max_sell-prices[i]);
17            max_sell=max(max_sell,prices[i]);
18            res=max(res,sell1[i]+sell2[i]);
19        }
20        return res;
21     }
22 };

之前写过的python版本,以供参考:

 1 class Solution:
 2     def maxProfit(self, prices) -> int:
 3         l=len(prices)
 4         if l<2:
 5             return 0
 6         res=0
 7         #先算一个dp1数组
 8         #dp1[i]表示截止到第i-1天只进行一次买卖的最大利润
 9         dp1=[0 for i in range(l)]
10         max_price,min_price=prices[0],prices[0]
11         for i in range(1,l):
12             dp1[i]=max(dp1[i-1],prices[i]-min_price)
13             #对于第i天来说,1.如果当天卖:最大利润即当前卖出价格
14             #减去之前的最小买入价格,2如果不卖:最大利润和前一天的
15             #最大利润相同
16             min_price=min(min_price,prices[i])  #更新当前最小买入价格
17             max_price=max(max_price,prices[i])  #更新当前最大卖出价格
18         #对于任意k,dp1[k]表示k卖出的最大利润,
19         #那么需要求剩下k+1到n-1的最大利润
20         #倒着求,因为右边界不变始终为l-1,左边界在变化
21         #dp2[i]表示从i开始到最后只进行一次买卖的最大利润
22         res=dp1[-1]
23         # print(res)
24         dp2=[0 for i in range(l)]
25         max_price=prices[-1]
26         for i in range(l-2,-1,-1):
27             dp2[i]=max(dp2[i+1],max_price-prices[i])
28             #对于第i天,1.若当天买,则最大利润即之后的最大卖出价格减去
29             #当前买入价格,2.若当天不买,最大利润和后一天的最大利润相同
30             max_price=max(max_price,prices[i])  #更新当前最大卖出价格
31             res=max(res,dp1[i-1]+dp2[i]) if i>=1 else max(res,dp2[i])
32         # print(dp1)
33         # print(dp2)
34         return res

 

后来发现其实不需要存储dp数组,只需要一个变量记录上一次的状态就行,但我懒,就不写了,反正内存大任性

 

题目4----188. 买卖股票的最佳时机IV:

链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/

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

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

 

这道题限制买卖次数最大为k,是一个变量。

 

设二维dp数组。dp[i][j]表示截止到i最多完成j笔交易的最大利润。

截止第i天,最大利润首先可以是之前的最大利润,即之前就完成了j笔交易,第i天不卖。

当然第i天也可以卖,那么本拨交易的买入最晚也得是i-1天,那么i-2天之前(包含i-2天)就必须要完成j-1笔交易。

用一个max值表示截止到前一天完成j-1笔交易、并且是待卖出状态的最大利润。

递推方程为:dp[i][j]=max(dp[i-1][j],max+prices[i])

另外每次循环中要更新max的值。

 

另外有用例k给的无限大,那么申请dp数组时会爆栈。需要判断一下k和价格数量的关系,如果k太大,转化为上面第2题的无限次的买卖股票问题。

前面的问题1和问题3只是这道题的特殊情况,代码直接复制过去就可以运行。

 1 class Solution {
 2 public:
 3     int maxProfit(int k, vector<int>& prices) {
 4         if(prices.empty()){
 5             return 0;
 6         }
 7         int n=prices.size();
 8         if(2*k<n){
 9             vector<vector<int>> dp(n,vector<int>(k+1,0));
10             //dp[i][j]表示截止第i天完成最多j次交易得到的最大收益
11             for(int j=1;j<k+1;++j){
12                 int _max=-prices[0];
13                 for(int i=1;i<n;++i){
14                     dp[i][j]=max(dp[i-1][j],_max+prices[i]);
15                     _max=max(dp[i-1][j-1]-prices[i],_max);
16                 }
17             }
18             return dp.back().back();
19         }
20         else{//k太大,相当于可以无限次交易
21             vector<int> dp(n,0);
22             //dp[i]表示截止第i+1天得到的最大收益
23             int _max=-prices[0];
24             for(int i=1;i<n;++i){
25                 dp[i]=max(dp[i-1],_max+prices[i]);
26                 _max=max(_max,dp[i]-prices[i]);
27             }
28             return dp.back();
29         }
30         return 0;
31     }
32 };

 

题目5----309. 最佳买卖股票时机含冷冻期:

链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

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

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

 

这道题多了一个冷冻期的限制:卖出之后要至少休息一天才能买入

那么理所当然的,多设立一个dp数组以表示冷冻期的状态:

 1 class Solution {
 2 public:
 3     int maxProfit(vector<int>& prices) {
 4         if(prices.size()<2){
 5             return 0;
 6         }
 7         int n=prices.size();
 8         vector<int> buy(n,0),sell(n,0),freeze(n,0);
 9         //buy[i]、sell[i]、freeze[i]分别表示第i天
10         //买入、卖出、不买不卖的最大收益(包含买股票花的钱)
11         buy[0]=-prices[0];
12         int res=0;
13         for(int i=1;i<n;++i){
14             buy[i]=max(buy[i-1],freeze[i-1]-prices[i]);
15             sell[i]=max(sell[i-1],buy[i-1]+prices[i]);
16             freeze[i]=max(sell[i-1],freeze[i-1]);
17             res=max(sell[i],res);
18         }
19         return res;
20     }
21 };

也可以仿照第四题的解法,用一个max值保存截止前一天待卖出状态的最大利润,不过更新max的时候,要注意冷冻期的要求。所以_max=max(_max,dp[i-2]-prices[i]),即截止i-2天卖出的最大利润,休息一天,第i天买入。之前的题目是_max=max(_max,dp[i-1]-prices[i]),注意二者的区别,虽然只是1和2的数字不同,但却是整道题的关键

 1 class Solution {
 2 public:
 3     int maxProfit(vector<int>& prices) {
 4         int n=prices.size();
 5         if(n<2){return 0;}
 6         vector<int> sell(n,0);
 7         sell[1]=max(0,prices[1]-prices[0]);
 8         int _max=max(-prices[0],-prices[1]);
 9         for(int i=2;i<n;++i){
10             sell[i]=max(sell[i-1],_max+prices[i]);
11             _max=max(_max,sell[i-2]-prices[i]);
12         }
13         // for(int x:sell){cout<<x<<" ";};
14         return sell[n-1];
15     }
16 };

完结撒花????

 

明天就要华为机试了,坏运气给??爬

2020-02-19 02:01:09

以上是关于Leetocode全部5道买卖股票问题总结(121+122+123+188+309)的主要内容,如果未能解决你的问题,请参考以下文章

leetcode121 买卖股票的最佳时机(Easy)

Leetcode 121.买卖股票的最佳时机

121. 买卖股票的最佳时机

121. 买卖股票的最佳时机

leetcode 121. 买卖股票的最佳时机

LeetCode——121. 买卖股票的最佳时机