题目描述
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
输入: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 。
示例 2:
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
最大总利润为 4 。
示例 3:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0。
提示:
- 1 <= prices.length <= 3 * 104
- 0 <= prices[i] <= 104
题目思路
前面还有一题 121. 买卖股票的最佳时机
比较简单没有记录,其条件是只允许买入一次和卖出一次,这里是可以多次买入和卖出。可以把每次买入和卖出都看成是一次交易。
贪心算法
而这里题干中说了在每一天可以决定是否购买和/或出售股票,实际上只需要在价格上涨前时候买入,在价格下跌前卖出就可以了。也就是在每次价格上涨的时候都进行一次交易。
所以这题第一反应是贪心算法,也就是每次只要价格上涨就进行交易。可以遍历整个数组,判断当前价格和前一个价格的大小关系,如果当前价格大于前一个价格,就将差值累加到结果中。
1 | func maxProfit(prices []int) int { |
这里其实也就是找到这个数组里每一个长度为 2
的上升子序列,然后累加子序列的差值即可。即便📈连续上升,也不会影响结果,比如 [1,2,3,4,5]
,在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。而如果累加 2-1
、3-2
、4-3
、5-4
,也会得到 4
。所以这里的贪心算法是正确的。
动态规划
如果不使用贪心算法的话,可以使用动态规划的方法。说实话这个题目用动态规划的方法来做有点大材小用。因为这里的贪心算法已经可以很简单的解决这个问题了。但遇到动态规划的问题还是要考虑动态规划的方法。动态规划的核心思想是将一个复杂的问题分解成简单的子问题,然后通过解决子问题来解决原问题。这里其实最关键的就是要想到:
- 每天手上的股票只会有两种状态:持有股票和不持有股票。也就是
dp[i][0]
表示第i
天不持有股票的最大利润,dp[i][1]
表示第i
天持有股票的最大利润。 - 第
i
天不持有股票的状态只会通过两种状态转移过来:第i-1
天没有持有股票且第i
天也没有买入股票,或者第i-1
天持有股票而第i
天卖出股票。也就是dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
。 - 第
i
天持有股票的状态只会通过两种状态转移过来:第i-1
天持有股票且第i
天也没有卖出股票,或者第i-1
天没有持有股票而第i
天买入股票。也就是dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
。 - 这里的
dp[i][0]
和dp[i][1]
的初始值是0
和-prices[0]
,也就是第0
天不持有股票的最大利润为0
,持有股票的最大利润为-prices[0]
。 - 最终的结果就是
dp[len(prices)-1][0]
,也就是最后一天不持有股票的最大利润。
所以这里可以写出代码:
1 | func maxProfit(prices []int) int { |
当然还可以优化,上面代码时间复杂度是 O(n),空间复杂度是 O(1),但是因为只需要保存前一天的状态,所以可以只用两个变量来保存前一天的状态。
1 | func max(a, b int) int { |
这样就可以将空间复杂度降到 O(1) 了。