买卖股票问题
今天重新写了些动态规划和贪心的题目,买卖股票的问题在这两个方面还是挺经典的。买卖股票在力扣上有四种题目,难度也是有涉及到简单、中等和困难。既然写了那么多次这几道题目,索性就水一篇文章来总结下买卖股票题的做法吧
买卖股票的最佳时机
题目连接:买卖股票的最佳时机
描述:
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
1
2 输入:[7,1,5,3,6,4]
输出:5
作为股票题的第一题很简单,根据题目的描述,由于只进行一次交易,所以只需找到最低价买入,最高价卖出就行了。
1 | class Solution { |
这题也可以用动态规划的做法来做,只不过上面的贪心算法会更加的快更容易想。这里也写上动态规划的代码
1 | class Solution { |
买卖股票的最佳时机 II
题目链接:买卖股票的最佳时机 II
描述:
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
1
2 输入:prices = [7,1,5,3,6,4]
输出:7
这一题与股票1的差别在于没有限制最多交易的次数,即我们可以多次进行买卖,这一题动态规划的做法我觉得是相对比较容易想出来的,定义一个二维数组dp[n][2],dp[i][0]表示当前不持有股票的最大利润,dp[i][1]表示当前持有股票的最大利润。dp[i][0]可以是之前不持有股票的利润,也可以是把之前的股票在当前卖出后的利润,取它们之间的最大值即可,dp[i][1]可以是之前持有股票的利润,也可以是在之前没有持有股票买入当前股票后的利润,也是取它们之间的最大值即可。就可以得出这么一个状态转移方程:
状态方程得出来之后也很容易写出代码:
1 | class Solution { |
这题也是可以用贪心的做法来做的,我们可以把利润的获取拆分开来,假设我们有1、5、6这三个价格,一眼看上去我们是在价格为1时买入,价格为6时卖出,我们可以把这个过程看成在5的时候价格比1高,我们在5卖出,卖出后再把5作为买入价格,到6的时候价格比5高我们卖出股票,这个过程的利润就可以看成(5-1)+(6-5)
,也就是6-1
。所以我们只需把每天的正利润累加起来即可得到正确答案。代码如下:
1 | class Solution { |
买卖股票的最佳时机 III
题目链接:买卖股票的最佳时机 III
描述:
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
1
2 输入:prices = [3,3,5,0,0,3,1,4]
输出:6
这道题跟股票II相比区别在于这里限制了交易次数是最多两次,也就是我们不能像股票II那样去贪心了,那就使用动态规划的做法来写。因为限制交易次数,状态可以分为:不操作、第一次交易买入、第一次交易卖出、第二次交易买入和第二次交易卖出这五种状态。用二维数组表示的话就是dp[n][5],其中dp[i][0]代表不操作,dp[i][1]代表在第i天第一次交易中是有买入股票,但要注意不一定是在第i天买的,dp[i][2]代表在第i天第一次交易已卖出,也要注意不一定是在第i天卖出。dp[i][3]和dp[i][4]的意思也跟上面一样,区别在于是代表第二次交易,dp[i][3]的更新会需要有dp[i-1][2]的参与,因为这次的买入后利润是要基于第一次交易完成后的利润来计算的。代码就如下:
1 | class Solution { |
通过代码可以看到dp[i]的状态只依赖于dp[i-1],那么可以考虑缩减层次,这里可以用四个常量来表示后四种状态,代码如下:
1 | class Solution { |
买卖股票的最佳时机 IV
题目链接:买卖股票的最佳时机 IV
描述:
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
1
2 输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
这题其实跟股票III差不多的,只不过最多交易次数从2变成了k这个不定量,思维还是相通的,通过列举k为2、3、4可以发现,在dp[i][j]中第二维的下标为奇数时都是代表股票买入的状态,下标为偶数(除了0)都是代表股票卖出的状态。于是我们的二维数组就可以申请为dp[n][2*k+1]。这里有一个思考点,就是k如果大于等于数组长度的一般的话,其实就变成跟股票II一样的题目没有限制最多交易次数了,因为买入卖出才算一次交易,也就是一次交易就占据了两天,如果最多次数超过数组长度的一半,那也就是相当于超过n天了,所以在k大于等于数组长度一半时直接采用贪心做法更加高效。代码如下:
1 | class Solution { |
跟上一题一样,也可以考虑对二维数组进行优化。这里的优化采用了两个一维数组buy[k+1]和sell[k+1]来代表在第i笔交易时买入股票和卖出股票时的最大利润,buy[i]的更新会参考上一笔交易卖出后的利润,所以容易得出buy的转移方程为buy[i] = max(buy[i],sell[i-1]-prices[j])
,而sell的更新很容易因为我们常规套路做多的思维而写成sell[i] = max(sell[i],buy[i-1]+prices[j])
,但要本次卖出股票只有在本次交易买入后才能卖出,而不能参考上一次买入股票时的利润,在执行本次卖出时上一次的交易已经是完成的了,所以是buy[i]+prices[j]。代码如下:
1 | class Solution { |
以上就是买卖股票四题的方法总结了。
参考
- 代码随想录——股票三
- 代码随想录——股票四
- 力扣题解及评论区
- 本文标题:买卖股票问题
- 本文作者:Johnson
- 创建时间:2022-09-07 22:36:49
- 本文链接:https://iconson.top/买卖股票问题/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!