Python 基础算法——买卖股票的最佳时机 II

17. 买卖股票的最佳时机 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 。总利润为 4 + 3 = 7 。

常用的算法分别是:贪心算法动态规划 谷峰法 

贪心算法:

它通过遍历整个股票价格数组,寻找所有的递增子序列,并计算每个递增子序列对应的利润,

并将所有利润相加得到最终的最大利润。

代码思路:
  1. 初始化变量 profit 为0,表示当前的最大利润。
  2. 对于 prices 数组中的每一天的股票价格,从第二天开始遍历。
  3. 如果当前股票价格比前一天的价格高,说明可以在前一天买入并在当前天卖出,从而获得利润,将这部分利润加到 profit 变量中。
  4. 最后返回 profit 即为最大利润。
# 贪心算法  时间复杂度:O(n)    空间复杂度:O(1)
def maxProfit1(prices):
    profit = 0
    # 特殊情况:数组个数为0/1
    if len(prices) <= 1:
        return profit
    
    for i in range(1, len(prices)):
        if prices[i] > prices[i-1]:
            profit += prices[i] - prices[i-1]

    return profit

时间复杂度: 代码中使用一个循环来遍历整个 prices 数组,对于每个股票价格进行了常数时间的操作。因此,时间复杂度为O(n),其中 n 是 prices 数组的大小。

空间复杂度: 代码只使用了一个额外变量 profit 来存储最大利润,而不需要使用额外的数据结构或数组。因此,空间复杂度为O(1),是一个常数级别的空间消耗。

动态规划1:

它通过构建一个动态规划表格来解决买卖股票的最佳时机问题。在每一天,我们根据两种选择(买入或卖出股票)来更新动态规划表格,然后利用中间结果的保存和复用,避免了重复计算,最终得到能获得的最大利润。

代码思路:
  1. 初始化一个大小为n的动态规划表格 dp,其中 dp[i] 表示第i天结束时的最大利润。
  2. 对于 prices 数组中的每一天的股票价格,从第二天开始遍历。
  3. 如果今天的股票价格比昨天高,说明可以在昨天买入并在今天卖出,从而获得利润。因此,dp[i] 更新为上一次持有股票的最大利润加上今天的利润(即 dp[i-1] + prices[i] - prices[i-1])。
  4. 如果今天的股票价格不比昨天高,说明不能卖出,所以 dp[i] 维持为上一次的最大利润(即 dp[i-1])。
  5. 最后返回 dp[-1] 即为最大利润。
# 动态规划1  时间复杂度:O(n)    空间复杂度:O(n)
def max_profit2(prices):
    n = len(prices)
    # 特殊情况:数组个数为0/1
    if n <= 1:
        return 0
    
    # 初始化动态规划表格
    dp = [0] * n

    for i in range(1, n):
        # 如果股票价格上涨,则卖出股票,计算利润
        if prices[i] > prices[i-1]:
            dp[i] = dp[i-1] + prices[i] - prices[i-1]
        else:
            dp[i] = dp[i-1]
    
    return dp[-1]

时间复杂度: 代码中使用了一个循环来遍历整个 prices 数组,对于每个股票价格进行了常数时间的操作。因此,时间复杂度为O(n),其中 n 是 prices 数组的大小。

空间复杂度: 代码使用了一个大小为n的动态规划表格 dp 来存储中间结果,需要额外的空间来保存这个表格。因此,空间复杂度为O(n),与输入数组的大小成线性关系。

动态规划2:

它通过使用两个变量 f0f1 来存储中间结果,以实现对最大利润的更新。代码通过一次遍历 prices 数组来更新 f0f1 的值,最后返回 f0 即为最大利润。

代码思路:
  1. 首先,初始化两个变量 f0f1,分别表示两个阶段的最大利润。f0 表示未持有股票时的最大利润,而 f1 表示持有股票时的最大利润。

  2. 对于 prices 数组中的每一天的股票价格,从头到尾进行遍历。

  3. 对于第 i 天,我们有两种选择:

    • 如果今天不持有股票,则 f0 可能是昨天不持有股票的利润 f0,或者昨天持有股票并在今天卖出的利润 f1 + p 中的较大值。即 f0 = max(f0, f1 + p)。这个操作相当于在前一天不持有股票时保持不变,或者在前一天持有股票并在今天卖出获得利润。
    • 如果今天持有股票,则 f1 可能是昨天持有股票的利润 f1,或者昨天不持有股票并在今天买入的利润 f0 - p 中的较大值。即 f1 = max(f1, f0 - p)。这个操作相当于在前一天持有股票时保持不变,或者在前一天不持有股票并在今天买入获得利润。
  4. 最后返回 f0,即为最大利润。

# 动态规划2(超简化版)  时间复杂度:O(n)    空间复杂度:O(1)
def maxProfit(prices):
    f0, f1 = 0, -inf
    for p in prices:
        f0, f1 = max(f0, f1 + p), max(f1, f0 - p)
    return f0

时间复杂度: O(n),因为它只对 prices 数组进行了一次遍历。

空间复杂度:O(1),因为只使用了两个额外的变量 f0f1 来存储中间结果,没有使用额外的数据结构或数组。

峰谷法:

它通过利用峰谷之间的差值来计算最大利润。

代码思路:
  1. 首先,初始化一个变量 total_profit 为0,用于累积总利润。

  2. 对于 prices 数组中的每一天的股票价格,使用一个循环从头到尾进行遍历。

  3. 在循环中,首先寻找谷值:即找到一个价格 valley,满足在之后的价格中是一个相对低点(之后的价格递增)。

  4. 然后,寻找峰值:即找到一个价格 peak,满足在之后的价格中是一个相对高点(之后的价格递减)。

  5. 计算峰谷差值 peak - valley,并将其累加到 total_profit 中。

  6. 重复步骤3到步骤5,直到遍历完整个 prices 数组。

  7. 最后返回 total_profit,即为最大利润。

# 峰谷法  时间复杂度:O(n)    空间复杂度:O(1)
def max_profit3(prices):
    n = len(prices)
    if n <= 1:
        return 0
    
    total_profit = 0
    i = 0

    while i < n -1:
        # 寻找谷值
        while i < n -1 and prices[i] >= prices[i+1]:
            i += 1
        valley = prices[i]
        # 寻找峰值
        while i < n - 1 and prices[i] <= prices[i+1]:
            i += 1
        peak = prices[i]
        # 计算峰谷差值,并累加到总利润
        total_profit += peak - valley

    return total_profit

时间复杂度:O(n),其中 n 是 prices 数组的大小。算法通过一次遍历 prices 数组来寻找峰值和谷值,并计算峰谷差值,因此时间复杂度为线性级别。

空间复杂度:O(1)。算法只使用了常数个额外变量来存储中间结果,而不需要使用额外的数据结构或数组。因此,空间复杂度是常数级别的,与输入数组的大小无关。

题目来源:力扣

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值