文章目录
- Leetcode 121. 买卖股票的最佳时机
- Leetcode 122.买卖股票的最佳时机II
Leetcode 121. 买卖股票的最佳时机
题目链接: Leetcode 121. 买卖股票的最佳时机
题目描述: 给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。你只能选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
思路: 本题可以用贪心来做,贪心策略:在最便宜的一天买入,然后在该天之后最高的一天(不一定是所有元素最大值)卖出。
代码如下:(贪心)
class Solution {
public:int maxProfit(vector<int>& prices) {int low = INT_MAX;int result = 0;// 贪心策略:在最便宜的一天买入,然后在该天之后最高的一天(不一定是所有元素最大值)卖出for (int i = 0; i < prices.size(); i++) {low = min(low, prices[i]); // 维护最小值result = max(result, prices[i] - low); // 求最大收益}return result;}
};
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
贪心的做法关键在于想到合适的贪心策略。如果没想到贪心策略,本题也可以通过动态规划来解决:
dp[i][0]
: 表示第i
天持有股票所得最多现金;dp[i][1]
表示第i
天不持有股票所得最多现金- 递推公式: 如果第
i
天持有股票,则有dp[i][0] = max(dp[i - 1][0], -prices[i])
;如果第i
天不持有股票,则有dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0])
- 初始化:
dp[0][0] = -prices[0]
,dp[0][1] = 0
- 遍历顺序:从前向后遍历
代码如下:(dp)
class Solution {
public:int maxProfit(vector<int>& prices) {int n = prices.size();vector<vector<int>> dp(n, vector<int>(2));// dp[i][0]代表第i天持有股票;dp[i][1]代表第i天不持有股票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]; // 无论如何最后都要卖出股票}
};
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
我们根据递推公式发现,dp[i]
只取决于dp[i - 1]
的状态,因此只需要大小为2
的数组即可。
代码如下:(dp+滚动数组优化)
class Solution {
public:int maxProfit(vector<int>& prices) {int n = prices.size();vector<vector<int>> dp(2, vector<int>(2));// dp[i][0]代表第i天持有股票;dp[i][1]代表第i天不持有股票dp[0][0] = -prices[0];dp[0][1] = 0;for (int i = 1; i < n; i++) {dp[i % 2][0] = max(dp[(i - 1) % 2][0], -prices[i]);dp[i % 2][1] = max(dp[(i - 1) % 2][1], prices[i] + dp[(i - 1) % 2][0]);}return dp[(n - 1) % 2][1]; // 无论如何最后都要卖出股票}
};
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
Leetcode 122.买卖股票的最佳时机II
题目链接: Leetcode 122.买卖股票的最佳时机II
题目描述: 给你一个整数数组 prices
,其中 prices[i]
表示某支股票第 i
天的价格。在每一天,你可以决定是否购买或出售股票。你在任何时候最多只能持有一股股票。你也可以先购买,然后在同一天出售。返回你能获得的最大利润 。
思路: 本题我之前用贪心实现过:可以看这里,因此这里主要介绍动态规划的做法。
本题与上道题的动态规划思路基本相同,唯一的区别在于:由于可以多次买卖股票,因此推导dp[i][0]
时,需要考虑第i
天买入股票的情况,单纯用-prices[i]
表示就不可以了。
dp[i][0]
: 表示第i
天持有股票所得最多现金;dp[i][1]
表示第i
天不持有股票所得最多现金- 递推公式: 如果第
i
天持有股票,则有dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i])
;如果第i
天不持有股票,则有dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0])
- 初始化:
dp[0][0] = -prices[0]
,dp[0][1] = 0
- 遍历顺序:从前向后遍历
代码如下:(dp)
class Solution {
public:int maxProfit(vector<int>& prices) {int n = prices.size();vector<vector<int>> dp(n, vector<int>(2));// dp[i][0]代表第i天持有股票;dp[i][1]代表第i天不持有股票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]);//与121的唯一区别dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]);}return dp[n - 1][1];}
};
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
代码如下:(dp+滚动数组优化)
class Solution {
public:int maxProfit(vector<int>& prices) {int n = prices.size();vector<vector<int>> dp(2, vector<int>(2));// dp[i][0]代表第i天持有股票;dp[i][1]代表第i天不持有股票dp[0][0] = -prices[0];dp[0][1] = 0;for (int i = 1; i < n; i++) {dp[i % 2][0] = max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]);dp[i % 2][1] = max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i]);}return dp[(n - 1) % 2][1];}
};
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
总结: 这两道题不算难,不过需要理解好两道题的递推公式,不仅要知其然,还要知其所以然。
最后,如果文章有错误,请在评论区或私信指出,让我们共同进步!