当前位置:网站首页>Leetcode(122)——买卖股票的最佳时机 II
Leetcode(122)——买卖股票的最佳时机 II
2022-06-26 20:43:00 【SmileGuy17】
Leetcode(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 。
提示:
- 1 1 1 <= prices.length <= 3 ∗ 1 0 4 3 * 10^4 3∗104
- 0 0 0 <= prices[i] <= 1 0 4 10^4 104
题解
方法一:贪心
思路
因为由于股票的购买没有限制,因此整个问题等价于寻找 x x x 个不相交的区间 ( l i , r i ] (l_i,r_i] (li,ri] 使得如下的等式最大化
∑ i = 1 x a [ r i ] − a [ l i ] \sum_{i=1}^{x} a[r_i]-a[l_i] i=1∑xa[ri]−a[li]
其中 l i l_i li 表示在第 l i l_i li 天买入, r i r_i ri 表示在第 r i r_i ri 天卖出。
同时我们注意到对于 ( l i , r i ] (l_i,r_i] (li,ri] 这一个区间贡献的价值 a [ r i ] − a [ l i ] a[r_i]-a[l_i] a[ri]−a[li],其实等价于 ( l i , l i + 1 ] , ( l i + 1 , l i + 2 ] , … , ( r i − 1 , r i ] (l_i,l_i+1],(l_i+1,l_i+2],\ldots,(r_i-1,r_i] (li,li+1],(li+1,li+2],…,(ri−1,ri] 这若干个区间长度为 11 的区间的价值和,即
a [ r i ] − a [ l i ] = ( a [ r i ] − a [ r i − 1 ] ) + ( a [ r i − 1 ] − a [ r i − 2 ] ) + … + ( a [ l i + 1 ] − a [ l i ] ) a[r_i]-a[l_i]=(a[r_i]-a[r_i-1])+(a[r_i-1]-a[r_i-2])+\ldots+(a[l_i+1]-a[l_i]) a[ri]−a[li]=(a[ri]−a[ri−1])+(a[ri−1]−a[ri−2])+…+(a[li+1]−a[li])
因此 问题可以简化 为 找 x x x 个长度为 1 1 1 的区间 ( l i , l i + 1 ] (l_i,l_i+1] (li,li+1] 使得 ∑ i = 1 x a [ l i + 1 ] − a [ l i ] \sum_{i=1}^{x} a[l_i+1]-a[l_i] ∑i=1xa[li+1]−a[li] 价值最大化。
贪心的角度考虑我们每次选择贡献大于 0 0 0 的区间即能使得答案最大化,因此最后答案为
ans = ∑ i = 1 n − 1 max { 0 , a [ i ] − a [ i − 1 ] } \textit{ans}=\sum_{i=1}^{n-1}\max\{0,a[i]-a[i-1]\} ans=i=1∑n−1max{ 0,a[i]−a[i−1]}
其中 n n n 为数组的长度。
需要说明的是,该贪心算法只能用于计算最大利润,计算的过程并不是实际的交易过程。
考虑题目中的例子 [ 1 , 2 , 3 , 4 , 5 ] [1,2,3,4,5] [1,2,3,4,5],数组的长度 n = 5 n=5 n=5,由于对所有的 1 ≤ i < n 1 \le i < n 1≤i<n 都有 a [ i ] > a [ i − 1 ] a[i]>a[i-1] a[i]>a[i−1],因此答案为
ans = ∑ i = 1 n − 1 a [ i ] − a [ i − 1 ] = 4 \textit{ans}=\sum_{i=1}^{n-1}a[i]-a[i-1]=4 ans=i=1∑n−1a[i]−a[i−1]=4
但是实际的交易过程并不是进行 4 4 4 次买入和 4 4 4 次卖出,而是在第 1 1 1 天买入,第 5 5 5 天卖出。
代码实现
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ans = 0, size = prices.size();
if(size <= 1) return 0;
for (int i = 1; i < size; i++) {
if (prices[i] > prices[i-1]) {
// 卖出有利可图
ans += (prices[i] - prices[i-1]);
}
}
return ans;
}
};
Leetcode 官方题解:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ans = 0;
int n = prices.size();
for (int i = 1; i < n; ++i) {
ans += max(0, prices[i] - prices[i - 1]); // 相邻两者的差为降序则+0
}
return ans;
}
};
复杂度分析
时间复杂度: O ( n ) O(n) O(n),其中 n n n 为数组的长度。我们只需要遍历一次数组即可。
空间复杂度: O ( 1 ) O(1) O(1)。只需要常数空间存放若干变量。
方法二:动态规划
思路
考虑到「不能同时参与多笔交易」,因此每天交易结束后只可能存在手里有一支股票或者没有股票的状态。
定义状态 dp [ i ] [ 0 ] \textit{dp}[i][0] dp[i][0] 表示第 i i i 天交易完后手里没有股票的最大利润, dp [ i ] [ 1 ] \textit{dp}[i][1] dp[i][1] 表示第 i i i 天交易完后手里持有一支股票的最大利润( i i i 从 0 0 0 开始)。
考虑 dp [ i ] [ 0 ] \textit{dp}[i][0] dp[i][0] 的转移方程,如果这一天交易完后手里没有股票,那么可能的转移状态为前一天已经没有股票,即 dp [ i − 1 ] [ 0 ] \textit{dp}[i-1][0] dp[i−1][0],或者前一天结束的时候手里持有一支股票,即 dp [ i − 1 ] [ 1 ] \textit{dp}[i-1][1] dp[i−1][1],这时候我们要将其卖出,并获得 prices [ i ] \textit{prices}[i] prices[i] 的收益。因此为了收益最大化,我们列出如下的状态转移方程:
dp [ i ] [ 0 ] = max { dp [ i − 1 ] [ 0 ] , dp [ i − 1 ] [ 1 ] + prices [ i ] } \textit{dp}[i][0]=\max\{\textit{dp}[i-1][0],\textit{dp}[i-1][1]+\textit{prices}[i]\} dp[i][0]=max{ dp[i−1][0],dp[i−1][1]+prices[i]}
再来考虑 dp [ i ] [ 1 ] \textit{dp}[i][1] dp[i][1],按照同样的方式考虑转移状态,那么可能的转移状态为前一天已经持有一支股票,即 dp [ i − 1 ] [ 1 ] \textit{dp}[i-1][1] dp[i−1][1],或者前一天结束时还没有股票,即 dp [ i − 1 ] [ 0 ] \textit{dp}[i-1][0] dp[i−1][0],这时候我们要将其买入,并减少 prices [ i ] \textit{prices}[i] prices[i] 的收益。可以列出如下的状态转移方程:
dp [ i ] [ 1 ] = max { dp [ i − 1 ] [ 1 ] , dp [ i − 1 ] [ 0 ] − prices [ i ] } \textit{dp}[i][1]=\max\{\textit{dp}[i-1][1],\textit{dp}[i-1][0]-\textit{prices}[i]\} dp[i][1]=max{ dp[i−1][1],dp[i−1][0]−prices[i]}
对于初始状态,根据状态定义我们可以知道第 0 0 0 天交易结束的时候 dp [ 0 ] [ 0 ] = 0 \textit{dp}[0][0]=0 dp[0][0]=0, dp [ 0 ] [ 1 ] = − prices [ 0 ] \textit{dp}[0][1]=-\textit{prices}[0] dp[0][1]=−prices[0]。
因此,我们只要从前往后依次计算状态即可。由于全部交易结束后,持有股票的收益一定低于不持有股票的收益(买股票花钱,废话),因此这时候 dp [ n − 1 ] [ 0 ] \textit{dp}[n-1][0] dp[n−1][0] 的收益必然是大于 dp [ n − 1 ] [ 1 ] \textit{dp}[n-1][1] dp[n−1][1] 的,最后的答案即为 dp [ n − 1 ] [ 0 ] \textit{dp}[n-1][0] dp[n−1][0]。
注意到上面的状态转移方程中,每一天的状态只与前一天的状态有关,而与更早的状态都无关,因此我们不必存储这些无关的状态,只需要将 dp [ i − 1 ] [ 0 ] \textit{dp}[i-1][0] dp[i−1][0] 和 dp [ i − 1 ] [ 1 ] \textit{dp}[i-1][1] dp[i−1][1] 存放在两个变量中,通过它们计算出 dp [ i ] [ 0 ] \textit{dp}[i][0] dp[i][0] 和 dp [ i ] [ 1 ] \textit{dp}[i][1] dp[i][1] 并存回对应的变量,以便于第 i + 1 i+1 i+1 天的状态转移即可。
代码实现
Leetcode 官方题解:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
int dp0 = 0, dp1 = -prices[0];
for (int i = 1; i < n; ++i) {
int newDp0 = max(dp0, dp1 + prices[i]);
int newDp1 = max(dp1, dp0 - prices[i]);
dp0 = newDp0;
dp1 = newDp1;
}
return dp0;
}
};
复杂度分析
时间复杂度: O ( n ) O(n) O(n),其中 n n n 为数组的长度。一共有 2 n 2n 2n 个状态,每次状态转移的时间复杂度为 O ( 1 ) O(1) O(1),因此时间复杂度为 O ( 2 n ) = O ( n ) O(2n)=O(n) O(2n)=O(n)。
空间复杂度: O ( n ) O(n) O(n)。我们需要开辟 O ( n ) O(n) O(n) 空间存储动态规划中的所有状态。如果使用空间优化,空间复杂度可以优化至 O ( 1 ) O(1) O(1)。
边栏推荐
- Redis + guava local cache API combination, performance burst!
- Bonne Recommandation: développer des outils de sécurité pour les terminaux mobiles
- Muke 8. Service fault tolerance Sentinel
- Detailed explanation of retrospective thinking
- 浏览器的垃圾回收机制
- 0 basic C language (1)
- 阿里云个人镜像仓库日常基本使用
- Leetcode question brushing: String 05 (Sword finger offer 58 - ii. left rotation string)
- [most detailed] the latest and complete redis interview (70)
- Flutter TextField详解
猜你喜欢
![[Bayesian classification 4] Bayesian network](/img/5b/348e00c920028e33ca457196586d36.png)
[Bayesian classification 4] Bayesian network

Gamefi active users, transaction volume, financing amount and new projects continue to decline. Can axie and stepn get rid of the death spiral? Where is the chain tour?

Leetcode: hash table 08 (sum of four numbers)

vue中缓存组件keep-alive

【山东大学】考研初试复试资料分享
![[Shandong University] information sharing for the first and second examinations of postgraduate entrance examination](/img/f9/68b5b5ce21f4f851439fa061b477c9.jpg)
[Shandong University] information sharing for the first and second examinations of postgraduate entrance examination

与 MySQL 建立连接

好物推薦:移動端開發安全工具

Distributed ID generation system

Introduction to single chip microcomputer one-on-one learning strategy, independent development program immediately after reading
随机推荐
定长内存池
C language simple login
12个MySQL慢查询的原因分析
[serialization] how to master the core technology of opengauss database? Secret 5: master database security (6)
[Bayesian classification 3] semi naive Bayesian classifier
On the origin of the dispute between the tradition and the future of database -- AWS series column
30. 串联所有单词的子串
710. random numbers in the blacklist
Introduction to single chip microcomputer one-on-one learning strategy, independent development program immediately after reading
Detailed explanation of shutter textfield
Twenty five of offer - all paths with a certain value in the binary tree
【贝叶斯分类3】半朴素贝叶斯分类器
30. concatenate substrings of all words
Many gravel 3D material mapping materials can be obtained with one click
SentinelResource注解詳解
【protobuf 】protobuf 升级后带来的一些坑
这些地区考研太疯狂!哪个地区报考人数最多?
Can I open an account online? Is it safe?
Leetcode question brushing: String 06 (implement strstr())
【 protobuf 】 quelques puits causés par la mise à niveau de protobuf