LeetCode:121. 买卖股票的最佳时机(python)

LeetCode:121. 买卖股票的最佳时机(python)

给定一个数组,它的第 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。

LeetCode 链接


思路1:
  • 从后往前遍历,通过已找到的最高股票价格更新最大利润值,随后查看是否需要更新最高股票价格,若是,则更新。
附代码1(Python):
class Solution:
    def maxProfit(self, prices):
        if not prices:
            return 0
        res = 0                                  # 初始化最大利润
        max_p = prices[-1]                       # 初始化最大股票价格
        for i in range(len(prices)-2, -1, -1):   # 从后往前遍历  
            res = max(res, max_p-prices[i])      # 更新最大利润
            max_p = max(max_p, prices[i])        # 更新最高股票价格
        return res
test = Solution()
prices_li = [[7,1,5,3,6,4],
             [7,6,4,3,1]
            ]
for prices in prices_li:
    print(test.maxProfit(prices))
5
0
思路2:动态规划
1. 分析股票交易动态规划方案
  • 状态

    • 天数
    • 交易次数
    • 持有状态(1表示持有,0表示未持有)

    dp[i][k][0 或 1] 表示第 i 天,第 k 次交易,持有或未持有的最大收益,最多有 n*K*2 中状态,n 为总天数,K 为最多交易次数。

  • 选择

    • 买入,buy
    • 卖出,sell
    • 无操作,rest

    dp[i][k][0] 时为未持有状态,则有两种选择 max(无操作,买入);在 dp[i][k][1] 时为持有状态,则有两种选择 max(无操作,卖出)

  • 状态转移

    在这里插入图片描述

    得状态转移方程

    dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
    max( 选择 rest , 选择 sell )

    dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
    max( 选择 rest , 选择 buy )

    注意:buy 或 sell 之后交易次数 +1 (此处选择在 buy 之后记录交易次数)

  • 初始化:第 i 天的最大收益需要由第 i-1 天的最大收益转移

    • dpi=0 开始,则需要考虑 i=-1 的情况,第 0 天有两种选择,rest(无操作)或 buy(买入)。状态为 0 (未持有股票)时只能选择 rest,因此,dp[-1][k][0]=0表示未有收益产生,dp[-1][k][1]=负无穷保证 sell 不能被选到;状态为 1 (持有股票)时只能选择 buy,因此,dp[-1][k][1]=负无穷保证 rest 不能被选到,dp[-1][k-1][0]=0表示未有收益产生,此时选择 buy 表示买入起始股票,收益为负,即 - prices[i]

      注意:索引 -1 为 0 的前一位,并非倒数最后一位,通常做添加一行(或一列)处理

    • dpi=1 开始,则需要考虑 i=0 的情况,第 0 天有两种选择,rest(无操作)或 buy(买入),dp[0][k][0]=0表示状态为 0 (未持有股票)选择 rest ;dp[0][k][1]=-prices[0]表示状态为 1 (持有股票)选择 buy 表示买入起始股票,买入时收益为负。

  • 返回值dp[n-1][K][0],表示状态为最后一天(n-1)、最多交易次数(K)、未持有股票(0,已卖出股票)时的收益。

2. 分析完股票交易的背景,现在查看本题要求,K=1,则 k 有 0 或 1 两种状态。在 k = 0 时表示未有交易发生,则 dp[i-1][0][0]=0。观察状态转移方程,其余状态只剩下 k=1,因此,简化状态转移方程如下:

dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])

dp[i][1] = max(dp[i-1][1], -prices[i])

附代码2(Python):
class Solution:
    def maxProfit(self, prices):
        if not prices:
            return 0
        n = len(prices)
        # 初始化 i=0 的状态
        dp = [[0]*2 for _ in range(n)]
        dp[0][1] = -prices[0]
        # 从 i=1 处开始动态转移
        for i in range(1, n):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
            dp[i][1] = max(dp[i-1][1], -prices[i])
        return dp[n-1][0]
test = Solution()
prices_li = [[7,1,5,3,6,4],
             [7,6,4,3,1],
             [1,2]
            ]
for prices in prices_li:
    print(test.maxProfit(prices))
5
0
1
思路3:优化动态规划
  • 观察上述状态转移方程,发现当天状态只与前一天状态有关,因此可采用单变量代替 dp 数组。
附代码3.1(Python):从 i=1 处开始迭代
class Solution:
    def maxProfit(self, prices):
        if not prices:
            return 0
        # 初始化状态
        dp_i_0 = 0
        dp_i_1 = -prices[0]
        # 从 i=1 处开始迭代
        for i in range(1, len(prices)):
            dp_i_0 = max(dp_i_0, dp_i_1+prices[i])
            dp_i_1 = max(dp_i_1, -prices[i])
        return dp_i_0
test = Solution()
prices_li = [[7,1,5,3,6,4],
             [7,6,4,3,1],
             [1,2]
            ]
for prices in prices_li:
    print(test.maxProfit(prices))
5
0
1
附代码3.2(Python):从 i=0 处开始迭代
class Solution:
    def maxProfit(self, prices):
        if not prices:
            return 0
        # 初始化状态
        dp_i_0 = 0
        dp_i_1 = float('-inf')
        # 从 i=0 处开始迭代
        for i in range(len(prices)):
            dp_i_0 = max(dp_i_0, dp_i_1+prices[i])
            dp_i_1 = max(dp_i_1, -prices[i])
        return dp_i_0
test = Solution()
prices_li = [[7,1,5,3,6,4],
             [7,6,4,3,1],
             [1,2]
            ]
for prices in prices_li:
    print(test.maxProfit(prices))
5
0
1

总结:

本题可以采用思路 1 的简单方法,介绍动态规划方法是因为 LeetCode 有一系列的股票题,可使用动态规划进行通解,可以更好地学习动态规划。

其他股票题的笔记如下:

LeetCode:121. 买卖股票的最佳时机(python)

LeetCode:122. 买卖股票的最佳时机 II(python)

LeetCode:123. 买卖股票的最佳时机 III(python)

LeetCode:188. 买卖股票的最佳时机 IV(python)

LeetCode:309. 最佳买卖股票时机含冷冻期(python)

LeetCode:714. 买卖股票的最佳时机含手续费(python)


参考:

LeetCode 题解

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值