股票买卖问题
主要分成以下四个逐步进阶的版本
- 最多进行一次交易
- 可以进行无数次交易
- 最多进行两次交易
- 最多进行 k k k次交易
最多进行一次交易
LeetCode121. 买卖股票的最佳时机
给定一个数组
p
r
i
c
e
s
prices
prices,它的第
i
i
i个元素
p
r
i
c
e
s
[
i
]
prices[i]
prices[i]表示一支给定股票第
i
i
i天的价格。
你只能选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回
0
0
0。
思路
使用动态规划: f [ i ] f[i] f[i]:表示前 i i i天所能收获的最大收益;分两种情况,一是第 i i i天卖出,那么就要指导前 i − 1 i-1 i−1天的最小价格,这需要一个变量保存;二是第 i i i天不交易,那么 f [ i ] = f [ i − 1 ] f[i]=f[i-1] f[i]=f[i−1]。综上, f [ i ] = m a x ( f [ i − 1 ] , p r i c e s [ i ] − m i n P ) f[i]=max(f[i-1],prices[i]-minP) f[i]=max(f[i−1],prices[i]−minP)
代码
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[] f = new int[n+1];
for(int i = 1, minP = Integer.MAX_VALUE / 2; i <= n; i++) {
f[i] = Math.max(f[i-1], prices[i-1] - minP);
minP = Math.min(minP, prices[i-1]);
}
return f[n];
}
}
可以进行无数次交易
LeetCode122. 买卖股票的最佳时机Ⅱ
给定一个数组
p
r
i
c
e
s
prices
prices,其中
p
r
i
c
e
s
[
i
]
prices[i]
prices[i]表示股票第
i
i
i天的价格。
在每一天,你可能会决定购买和/或出售股票。你在任何时候最多只能持有一股股票。你也可以购买它,然后在同一天出售。
返回你能获得的最大利润 。
思路
贪心:每当后一天比当前天的股票价格高,那么就在这两天进行一次买卖交易。
代码
class Solution {
public int maxProfit(int[] prices) {
int res = 0;
for(int i = 0; i + 1 < prices.length; i++) {
if(prices[i+1] > prices[i]) {
res += prices[i+1] - prices[i];
}
}
return res;
}
}
最多进行两次交易
LeetCode123. 买卖股票的最佳实际Ⅲ
给定一个数组,它的第
i
i
i个元素是一支给定的股票在第
i
i
i天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成两笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
思路
前后缀分解:枚举第二次交易买入时机 i i i,那么此时所能获取的最大收益为 f [ i − 1 ] + m a x P − p r i c e s [ i ] f[i-1]+maxP-prices[i] f[i−1]+maxP−prices[i],其中 f [ i − 1 ] f[i-1] f[i−1]就是最多买卖一次的前 i − 1 i-1 i−1天的最大收益, m a x P maxP maxP是第 i i i天后的最大股票价格,这两者都可以提前预处理出来。
代码
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[] f = new int[n+1];
for(int i = 1, minP = Integer.MAX_VALUE; i <= n; i++) {
f[i] = Math.max(f[i-1], prices[i-1] - minP);
minP = Math.min(minP, prices[i-1]);
}
int res = 0;
for(int i = n, maxP = 0; i >= 1; i--) {
res = Math.max(res, f[i-1] + maxP - prices[i-1]);
maxP = Math.max(maxP, prices[i-1]);
}
return res;
}
}
最多进行k次交易
LeetCode188. 买卖股票的最佳时机Ⅳ
给定一个整数数组
p
r
i
c
e
s
prices
prices,它的第
i
i
i个元素
p
r
i
c
e
s
[
i
]
prices[i]
prices[i]是一支给定的股票在第
i
i
i天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成
k
k
k笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
思路
状态机型DP:
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示此时手中没有股票,且花了
i
i
i天,已完成
j
j
j笔交易;
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示此时手中有股票,且花了
i
i
i天,正在进行第
j
j
j笔交易
根据上图易知:
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
g
[
i
−
1
]
[
j
]
+
w
[
i
]
)
f[i][j]=max(f[i-1][j],g[i-1][j]+w[i])
f[i][j]=max(f[i−1][j],g[i−1][j]+w[i]);
g
[
i
]
[
j
]
=
m
a
x
(
g
[
i
−
1
]
[
j
]
,
f
[
i
−
1
]
[
j
−
1
]
−
w
[
i
]
)
g[i][j]=max(g[i-1][j],f[i-1][j-1]-w[i])
g[i][j]=max(g[i−1][j],f[i−1][j−1]−w[i])
另外,当
k
≥
n
2
k\ge\frac{n}{2}
k≥2n,此题可以看作是可以进行无数次交易的特殊情况
代码
class Solution {
public int maxProfit(int k, int[] prices) {
int n = prices.length;
if(k >= n / 2) {
int res = 0;
for(int i = 0; i + 1 < n; i++) {
res += Math.max(0, prices[i+1] - prices[i]);
}
return res;
}
int INF = (int)(1e8);
int[][] f = new int[n+1][k+1];
int[][] g = new int[n+1][k+1];
for(int i = 0; i <= n; i++) {
Arrays.fill(f[i], -INF);
Arrays.fill(g[i], -INF);
}
f[0][0] = 0;
int res = 0;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= k; j++) {
f[i][j] = Math.max(f[i-1][j], g[i-1][j] + prices[i-1]);
if(j >= 1) {
g[i][j] = Math.max(g[i-1][j], f[i-1][j-1] - prices[i-1]);
}
res = Math.max(res, f[i][j]);
}
}
return res;
}
}