122. 买卖股票的最佳时机 II
Posted 炫云云
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了122. 买卖股票的最佳时机 II相关的知识,希望对你有一定的参考价值。
122. 买卖股票的最佳时机 II
给定一个数组 prices
,其中 prices[i]
是一支给定股票第 i
天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入: prices = [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出,
这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,
这笔交易所能获得利润 = 6-3 = 3 。
股票买卖策略:
- 单独交易日: 设今天价格 p 1 p_{1} p1 、明天价格 p 2 p_{2} p2, 则今天买入、明天卖出可赚取金额 p 2 − p 1 p_{2}-p_{1} p2−p1 (负值代表亏损)。
- 连续上涨交易日:设此上张交易日股票价格分别为 p 1 , p 2 , … , p n p_{1}, p_{2}, \\ldots, p_{n} p1,p2,…,pn, 则第一天买最后一天卖收益最大,即 p n − p 1 ; p_{n}-p_{1} ; pn−p1; 等价于每天都买卖, 即 p n − p 1 = ( p 2 − p 1 ) + ( p 3 − p 2 ) + … + p_{n}-p_{1}=\\left(p_{2}-p_{1}\\right)+\\left(p_{3}-p_{2}\\right)+\\ldots+ pn−p1=(p2−p1)+(p3−p2)+…+ ( p n − p n − 1 ) \\left(p_{n}-p_{n-1}\\right)_{ } (pn−pn−1)。
- 连续下降交易日:则不买卖收益最大,即不会矢钱。
动态规划
根据 「力扣」第 121 题的思路,需要设置一个二维矩阵表示状态。
第 1 步:定义状态
状态 dp[i][j]
定义如下 :
dp[i][j]
表示到下标为 i
的这一天,持股状态为 j
时,我们手上拥有的最大现金数。
注意:限定持股状态为 j
是为了方便推导状态转移方程,这样的做法满足 无后效性。
其中:
- 第一维
i
表示下标为i
的那一天( 具有前缀性质,即考虑了之前天数的交易 ); - 第二维
j
表示下标为i
的那一天是持有股票,还是持有现金。这里0
表示持有现金(cash),1
表示持有股票(stock)。
第 2 步:状态转移方程
-
状态从持有现金(cash)开始,到最后一天我们关心的状态依然是持有现金(cash);
-
每一天状态可以转移,也可以不动。状态转移用下图表示:
第 i i i天不持股: d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0] = max(dp[i-1][0], dp[i-1][1] +prices[i]) dp[i][0]=max(dp[i−1][0],dp[i−1][1]+prices[i]),为第 i − 1 i-1 i−1天不持股或者 第 i − 1 i-1 i−1天持股出售。
第 i i i天持股: d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i]) dp[i][1]=max(dp[i−1][1],dp[i−1][0]−prices[i]),为第 i − 1 i-1 i−1天持股或者 第 i − 1 i-1 i−1天不持股买进。
第 3 步:确定初始值
- 如果什么都不做,
dp[0][0] = 0
; - 如果持有股票,当前拥有的现金数是当天股价的相反数,即
dp[0][1] = -prices[i]
;
class Solution:
def maxProfit(self, prices: List[int]) -> int:
Len = len(prices)
# 特殊判断
if Len<2:
return 0
dp = [[0]* 2 for _ in range(Len)]
#dp[i][0] 下标为 i 这天结束的时候,不持股,手上拥有的现金数
#dp[i][1] 下标为 i 这天结束的时候,持股,手上拥有的现金数
# 状态转移:0 → 1 → 0 → 1 → 0 → 1 → 0
#初始化:不持股显然为 0,持股就需要减去第 1 天(下标为 0)的股价
dp[0][0] = 0
dp[0][1] = -prices[0]
#从第 2 天开始遍历
for i in range(1,Len):
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])
return dp[-1][0]
优化空间
由于当前行只参考上一行,每一行就 2 个值,因此可以考虑使用「滚动变量」(「滚动数组」技巧)。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
Len = len(prices)
# 特殊判断
if Len<2:
return 0
# cash:持有现金
# hold:持有股票
# 状态转移:cash → hold → cash → hold → cash → hold → cash
cash = 0
hold = -prices[0]
preCash = cash
preHold = hold
#从第 2 天开始遍历
for i in range(1,Len):
cash = max(preCash, preHold +prices[i])
hold = max(preHold ,preCash-prices[i])
return cash
贪心算法
贪心算法的直觉:由于不限制交易次数,只要今天股价比昨天高,就交易。
下面对这个算法进行几点说明:
该算法仅可以用于计算,但 计算的过程并不是真正交易的过程,但可以用贪心算法计算题目要求的最大利润。下面说明等价性:以 [1, 2, 3, 4]
为例,这 4
天的股价依次上升,按照贪心算法,得到的最大利润是:
res = (prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])
= prices[3] - prices[0]
仔细观察上面的式子,按照贪心算法,在下标为 1
、2
、3
的这三天,我们做的操作应该是买进昨天的,卖出今天的,虽然这种操作题目并不允许,但是它等价于:在下标为 0
的那一天买入,在下标为 3
的那一天卖出。
- 为什么叫「贪心算法」
求解最优化问题的算法通常需要经过一系列的步骤,在每个步骤都面临多种选择。对于许多最优化问题,使用动态规划算法来求最优解有些杀鸡用牛刀了,可以使用更简单、更高效的算法。贪心算法(greedy algorithm)就是这样的算法,它在每一步都做出当时看起来最佳的选择。也就是说,它总是做出局部最优的选择,寄希望这样的选择能导致全局最优解。
- 这道题 「贪心」 的地方在于,对于 「今天的股价 - 昨天的股价」,得到的结果有 3 种可能:① 正数,② 0,③负数。贪心算法的决策是: 只加正数 。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
Len = len(prices)
# 特殊判断
if Len<2:
return 0
res = 0
for i in range(1,Len):
diff = prices[i] - prices[i - 1]
if diff >0:
res +=diff
return res
等价写法:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
Len = len(prices)
# 特殊判断
if Len<2:
return 0
res = 0
for i in range(1,Len):
res += max(prices[i] - prices[i - 1],0)
return res
参考
以上是关于122. 买卖股票的最佳时机 II的主要内容,如果未能解决你的问题,请参考以下文章