题目:121. 买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
分析
由于只允许完成一次交易(买入和卖出),因此直接穷举遍历每一种可能的交易,然后求出所有交易的最大利润。
具体的以[7,1,5,3,6,4]为例,遍历所有可能执行卖出的天数(第一天不能卖出),然后找出价格最低的买入天数,即可得到最大利润:
执行卖出的天数i | 价格最低的买入天数j | 最大利润prices[i]-prices[j] |
---|---|---|
2 | 1 | 1 - 7 = -6 |
3 | 2 | 5 - 1 = 4 |
4 | 2 | 3 - 1 = 2 |
5 | 2 | 6 - 1 = 5 |
6 | 2 | 4 - 1 = 3 |
这里需要两层循环。第一层循环遍历所有的卖出天数i
,第二层循环查找前i
天内价格最低的天数j
,最大利润为prices[i]-prices[j]
。
由于需要两层循环,上述方法时间复杂度为 Θ ( n 2 ) \Theta(n^2) Θ(n2);
由于只用到循环变量和一个变量存储最大利润,则空间复杂度为 Θ ( 1 ) \Theta(1) Θ(1)。
动态规划
利用动态规划的思想来降低时间复杂度。
可以看到,上述分析中,第二层循环查找的是前i
天内价格最低的天数。
状态定义
n = p r i c e s . s i z e ( ) n = prices.size() n=prices.size()
定义 d p 1 [ 0 , 1 , ⋯ , n − 1 ] dp1[0,1,\cdots,n-1] dp1[0,1,⋯,n−1], d p 1 [ i ] dp1[i] dp1[i]表示前 i i i天的最低价格。
定义 d p 2 [ 0 , 1 , ⋯ , n − 1 ] dp2[0,1,\cdots,n-1] dp2[0,1,⋯,n−1], d p 2 [ i ] dp2[i] dp2[i]表示第 i i i天卖出时获取的最大利润。
转移方程
d p 1 [ i ] = m i n ( d p 1 [ i − 1 ] , p r i c e s [ i ] ) dp1[i] = min(dp1[i-1], prices[i]) dp1[i]=min(dp1[i−1],prices[i])
d p 2 [ i ] = p r i c e s [ i ] − d p 1 [ i − 1 ] dp2[i] = prices[i] - dp1[i-1] dp2[i]=prices[i]−dp1[i−1]
边界条件
d p 1 [ 0 ] = p r i c e s [ 0 ] dp1[0] = prices[0] dp1[0]=prices[0]
d p 2 [ 0 ] = 0 dp2[0] = 0 dp2[0]=0
最优解
a n s = m a x ( d p 2 [ i ] ) , i = 0 , 1 , 2 , ⋯ , n − 1 ans = max(dp2[i]),i=0,1,2,\cdots,n-1 ans=max(dp2[i]),i=0,1,2,⋯,n−1
复杂度分析
由于只有一个循环变量,因此只需要遍历一次即可,时间复杂度为 Θ ( n ) \Theta(n) Θ(n)
申请了两个长度为 n n n的数组 d p 1 , d p 2 dp1, dp2 dp1,dp2,空间复杂度为 Θ ( n ) \Theta(n) Θ(n)
动态规划实现了以空间换时间的思想。
空间复杂度的改进
分析状态转移方程可以发现,状态 d p 1 [ i ] dp1[i] dp1[i]和 d p 2 [ i ] dp2[i] dp2[i]只与前一个状态和当前循环变量 p r i c e s [ i ] prices[i] prices[i]有关。
因此,可以进一步降低空间复杂度到 Θ ( 1 ) \Theta(1) Θ(1)级别。
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n == 0) {
return 0;
}
int ans = 0;//第i天卖出时的最大利润
int low = prices[0];//前i-1天的最低价格
for (int i = 1; i < prices.size(); i++) {
ans = max(ans, prices[i] - low);
low = min(low, prices[i]);
}
return ans;
}
};