121. 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1: 输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。 示例 2: 输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
思路
1. 确定dp数组(dp table)以及下标的含义
定义一个一维数组dp,其中dp[i]表示第i天卖出股票所能获得的最大利润。
2. 确定递推公式
其中,
minPrice是到第i天为止的最低股票价格。
3. dp数组如何初始化
dp[0]应初始化为0,因为第一天卖出股票利润为0。
4. 确定遍历顺序
从第二天开始遍历,即从i = 1开始,直到i = n-1,其中n是数组prices的长度。
5. 举例推导dp数组
以prices = [7, 1, 5, 3, 6, 4]为例,dp数组初始化为[0, 0, 0, 0, 0, 0]。
- 第2天(
i = 1):minPrice = 1,dp[1] = max(0, 1 - 1) = 0 - 第3天(
i = 2):minPrice = 1,dp[2] = max(0, 5 - 1) = 4 - 第4天(
i = 3):minPrice = 1,dp[3] = max(4, 3 - 1) = 4 - 第5天(
i = 4):minPrice = 1,dp[4] = max(4, 6 - 1) = 5 - 第6天(
i = 5):minPrice = 1,dp[5] = max(5, 4 - 1) = 5
最终,dp[n-1] = dp[5] = 5,即最大利润为5。
题解
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n == 0) return 0;
vector<int> dp(n, 0);
int minPrice = prices[0];
for (int i = 1; i < n; ++i) {
minPrice = min(minPrice, prices[i]);
dp[i] = max(dp[i-1], prices[i] - minPrice);
}
return dp[n-1];
}
};
优化思路
1. 确定dp数组(dp table)以及下标的含义
dp[i][0]:第i天持有股票所能获得的最大利润。dp[i][1]:第i天不持有股票所能获得的最大利润。
2. 确定递推公式
dp[i][0] = max(dp[i - 1][0], -prices[i]):第i天持有股票的最大利润是前一天持有股票的最大利润(即没有卖出)和第i天买入股票两者之间的较大值。dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]):第i天不持有股票的最大利润是前一天不持有股票的最大利润(即没有买入)和第i天卖出股票两者之间的较大值。
3. dp数组如何初始化
dp[0][0] = -prices[0]:第0天买入股票,所以利润为-prices[0]。dp[0][1] = 0:第0天不买入也不卖出,利润为0。
4. 确定遍历顺序
从第1天开始遍历,直到最后一天。
5. 举例推导dp数组
以prices = [7, 1, 5, 3, 6, 4]为例,
- 第1天:
dp[1][0] = max(-7, -1) = -1,dp[1][1] = max(0, 1 + (-7)) = 0 - 第2天:
dp[2][0] = max(-1, -5) = -1,dp[2][1] = max(0, 5 + (-1)) = 4 - 第3天:
dp[3][0] = max(-1, -3) = -1,dp[3][1] = max(4, 3 + (-1)) = 4 - 第4天:
dp[4][0] = max(-1, -6) = -1,dp[4][1] = max(4, 6 + (-1)) = 5 - 第5天:
dp[5][0] = max(-1, -4) = -1,dp[5][1] = max(5, 4 + (-1)) = 5
最终,dp[len - 1][1] = dp[5][1] = 5,即最大利润为5。
这种方法更加通用,适用于更复杂的股票交易问题,例如允许多次买入和卖出,有交易费用等。
优化题解
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n == 0) return 0;
vector<vector<int>> dp(n, vector<int>(2));
dp[0][0] = -prices[0];
dp[0][1] = 0;
for (int i = 1; i < n; ++i) {
dp[i][0] = max(dp[i - 1][0], -prices[i]);
dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
}
return dp[n - 1][1];
}
};
这种解法更加通用,并适用于下一题
122. 买卖股票的最佳时机 II
给你一个整数数组 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 。
思路
确定dp数组(dp table)以及下标的含义
dp[i][0]:第i天持有股票所能获得的最大利润。dp[i][1]:第i天不持有股票所能获得的最大利润。
确定递推公式
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])
dp数组如何初始化
dp[0][0] = -prices[0]:第0天买入股票,所以利润为-prices[0]。dp[0][1] = 0:第0天不买入也不卖出,利润为0。
确定遍历顺序
- 从第1天开始遍历,直到最后一天。
举例推导dp数组
- 以
prices = [7, 1, 5, 3, 6, 4]为例,最终dp[len - 1][1] = 7,即最大总利润为7。
题解
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n == 0) return 0;
vector<vector<int>> dp(n, vector<int>(2));
dp[0][0] = -prices[0];
dp[0][1] = 0;
for (int i = 1; i < n; ++i) {
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[n - 1][1];
}
};