给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 3 * 10 ^ 4
0 <= prices[i] <= 10 ^ 4链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii
方法1 贪心算法:
思路:
参考大佬题解:力扣题解
- 方法1 贪心算法:“贪心算法” 在每一步总是做出在当前看来最好的选择
- 贪心算法和动态规划相比,它既不看前面(也就是说它不需要从前面的状态转移过来),也不看后面(无后效性,后面的选择不会对前面的选择有影响),因此贪心算法时间复杂度一般是线性的,空间复杂度是常数级别的。
- 这道题 “贪心” 的地方在于,对于 “今天的股价 - 昨天的股价”,得到的结果有 3 种可能:(1)正数(2)
0
(3)负数。 - 贪心算法的决策是:只加正数,累加正向收益。
-
时间复杂度:O(N)
空间复杂度:O(1)
- 方法2 动态规划:
- ① 考虑到「不能同时参与多笔交易」,因此每天交易结束后只可能存在手里有一支股票或者没有股票的状态。
- ② 先定义持有股票或不持有股票的两种状态:
- dp[i][0] 表示第 i 天交易完后手里没有股票时,但前些天已经通过低买高卖积累过的最大利润
dp[i][1] 表示第 i 天交易完后手里持有一支股票时的最大利润(i从0开始。交易完后手里持有一支股票时的利润:可能是前些天通过低买高卖已经赚取过的差价,并且此时又另外低价买入了一支股票)
- dp[i][0] 表示第 i 天交易完后手里没有股票时,但前些天已经通过低买高卖积累过的最大利润
- ③ 考虑初始状态值:根据状态定义可知:第0天交易结束的时候 dp[0][0]=0,dp[0][1]=−prices[0] 买入股票
- ④ 考虑dp[i][0] 的转移方程:
- 如果这一天交易完后手里没有股票,那么可能的转移状态为前一天已经没有股票,即 dp[i−1][0];或者前些天已经通过低买高卖有了利润,即 dp[i−1][1],并且手里还新低价买入了一支股票,这时候我们要将其卖出,并获得 prices[i] 的收益。
- 考虑dp[i][1] 的转移方程:前一天已经持有一支股票,即 dp[i−1][1];或者前一天结束时并没有持有股票,但是"前些天"通过低买高卖已经累计过的现金利润,即 dp[i−1][0],这时我们要将买入一支股票,所以减少 prices[i] 的收益
-
⑤ 由于全部交易结束后,持有股票的收益一定低于不持有股票的收益,因此这时候 dp[n−1][0] 的收益必然是大于dp[n−1][1] 的,最后的答案即为 dp[n−1][0]。
-
时间复杂度:O(N)
空间复杂度:O(N)
Go版本 贪心算法&动态规划:
// 方法一 贪心算法:累加收益
func maxProfit(prices []int) int {
sum := 0
for i := 1; i < len(prices); i++ {
// 如果今天相对于昨天是正收益,那么就不断累加和;反之不处理
diff := prices[i] - prices[i - 1]
if diff > 0 {
sum += diff
}
}
return sum
}
// 方法二 动态规划:
func maxProfit(prices []int) int {
dp := make([][]int, len(prices))
for i := 0; i < len(prices); i++ {
// dp[i][0]:当前手中 没 股票(股票已全卖掉)时的最大利润
// dp[i][1]:当前手中 有 股票(股票还未全卖)时的最大利润
dp[i] = make([]int, 2)
}
// dp[0][1] = −prices[0]:第0天以prices[0]的价格买入股票
dp[0][0], dp[0][1] = 0, -prices[0]
// 题目要求:你在任何时候 最多 只能持有 一股 股票
for i := 1; i < len(prices); i++ {
// 当前手中没股票时的最大利润:
// dp[i-1][0]:前一天手中没有股票,且今天没有任何股票操作
// dp[i-1][1] + prices[i]:前一天手中还有股票,但今天以prices[i]的价格卖了
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
// 当前手中有股票时的最大利润:
// dp[i-1][0] - prices[i]:前一天手中没有股票,但今天以prices[i]的价格买入
// dp[i-1][1]:前一天手中持有股票,且今天没有任何股票操作
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
}
return dp[len(prices) - 1][0]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
C++版本 - 贪心算法:
// 贪心算法:初始i设为今天的股价(1),若今天股价较昨天有上涨,则累加,否则不操作
int maxProfit(vector<int>& prices) {
int res = 0;
for (int i = 1; i < prices.size(); i++)
{
int diff = prices[i] - prices[i - 1];
if (diff > 0)
{
res += diff;
}
}
return res;
}
方法2 动态规划:
题解还没完全理解,待更新~
补充:该题和LeetCode 121. 买卖股票的最佳时机 类似