LeetCode 121 买卖股票的最佳时机
给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
提示:
1 <= prices.length <= 10^5
0 <= prices[i] <= 10^4
问题分析
动态规划,子问题就是找子数组的最大利润dp[i]
,而最大利润可以用前一个子数组的最大利润与当前价格-当前买入价格(最小价格) 进行对比得出,因为只有当当前价格比之前更高时,才有希望得到比之前更高的利润。前一个子数组的最大利润为dp[i-1]
,当前价格为prices[i]
,当前买入价格为cost = min(cost,prices[i])
,状态转移方程如下:
d
p
[
i
]
=
m
a
x
(
d
p
[
i
−
1
]
,
p
r
i
c
e
s
[
i
]
−
m
i
n
(
c
o
s
t
,
p
r
i
c
e
s
[
i
]
)
)
dp[i]=max(dp[i-1],prices[i]-min(cost,prices[i]))
dp[i]=max(dp[i−1],prices[i]−min(cost,prices[i]))
这里实现需要注意的是:
- 这里使用了变量
cost
存储买入价格选择,避免了每次遍历数组寻找最小值,降低时间复杂度; - 在迭代更新
dp[i]
时cost
应是当前的最小价格,故应先更新cost
; - 状态转移方程中包含了
dp[i-1]
,这使得循环应从i=1
开始,需单独考虑n==1
时的返回值; - 由于循环从
i=1
开始,cost
的初始值应为prices[0]
class Solution {
public int maxProfit(int[] prices) {
// dp[i]=max(dp[i-1],prices[i]-min(cost,prices[i]))
int n = prices.length, cost = prices[0];
if(n==1) return 0;
int []dp = new int[n];
for(int i=1;i<n;i++){
cost = Math.min(cost,prices[i]);
dp[i]=Math.max(dp[i-1],prices[i]-cost);
}
return dp[n-1];
}
}
优化空间复杂度
滚动数组,观察可知,dp[i]
只与前面的dp[i-1]
有关,其余prices[i]
为需要遍历的对象——当前价格,cost
为当前买入价格,故可使用单个变量profit
替换掉dp
数组,这样还可以避免循环中的索引使用
p
r
o
f
i
t
=
m
a
x
(
p
r
o
f
i
t
,
p
r
i
c
e
−
m
i
n
(
c
o
s
t
,
p
r
i
c
e
)
)
profit=max(profit,price-min(cost,price))
profit=max(profit,price−min(cost,price))
class Solution {
public int maxProfit(int[] prices) {
// 滚动数组 profit = max(profit,price - min(cost,price))
int profit=0, cost = Integer.MAX_VALUE;
for(int price:prices){
cost = Math.min(cost,price);
profit = Math.max(profit,price - cost);
}
return profit;
}
}
LeetCode 122 买卖股票的最佳时机 II
给你一个整数数组 prices
,其中 prices[i]
表示某支股票第 i
天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
提示:
1 <= prices.length <= 3 * 10^4
0 <= prices[i] <= 10^4
class Solution {
public int maxProfit(int[] prices) {
if(prices.length==1) return 0;
int ans=0;
for(int i=1;i<prices.length;i++)
{
if(prices[i]>prices[i-1])
{
ans+= prices[i]-prices[i-1];
}
}
return ans;
}
}
class Solution {
public int maxProfit(int[] prices) {
if(prices.length==1) return 0;
int profit_0 = 0, profit_1 = -prices[0];
for(int i=1;i<prices.length;i++)
{
profit_0 = Math.max(profit_0,profit_1+prices[i]);
profit_1 = Math.max(profit_1,profit_0-prices[i]);
//profit_1<profit_1+prices[i],profit_0-prices[i]<profit_0,最后一定是profit_0大
}
return profit_0;
}
}
LeetCode 123 买卖股票的最佳时机III
给定一个数组,它的第 i
个元素是一支给定的股票在第 i
天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
提示:
1 <= prices.length <= 10^5
0 <= prices[i] <= 10^5
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
if(n==1) return 0;
int dp10 = -prices[0], dp01 = 0, dp11 = -prices[0], dp02 = 0;
//同一天可以买入卖出再买入
for(int i=1;i<n;i++)
{
// dp[i][1][0] = Math.max(dp[i-1][1][0],-prices[i]);
// dp[i][0][1] = Math.max(dp[i-1][0][1],dp[i-1][1][0]+prices[i]);
// dp[i][1][1] = Math.max(dp[i-1][1][1],dp[i-1][0][1]-prices[i]);
// dp[i][0][2] = Math.max(dp[i-1][0][2],dp[i-1][1][1]+prices[i]);
// dp[i][1][0] = Math.max(dp[i-1][1][0],-prices[i]);
// dp[i][0][1] = Math.max(dp[i-1][0][1],dp[i][1][0]+prices[i]);
// dp[i][1][1] = Math.max(dp[i-1][1][1],dp[i][0][1]-prices[i]);
// dp[i][0][2] = Math.max(dp[i-1][0][2],dp[i][1][1]+prices[i]);
dp10 = Math.max(dp10,-prices[i]);
dp01 = Math.max(dp01,dp10+prices[i]);
dp11 = Math.max(dp11,dp01-prices[i]);
dp02 = Math.max(dp02,dp11+prices[i]);
}
return dp02;
}
}