股票的最大利润(剑指 Offer 63)
1 题目描述
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,
最大利润 = 6-1 = 5 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
2 思路分析
使用暴力法第一份层循环指向买的一天,第二层循环一次遍历以后的每一天找到利润最大的一次交易可得时间复杂度为O(n²):
设共有n天,第a 天买,第b天卖,则需保证 a < b;可推出交易方案数共有(n-1)+(n-2)+···+2+1=(n-1)n/2。
class Solution {
public int maxProfit(int[] prices) {
int len = prices.length;
int res = 0;
int max = 0;
for(int i = 0;i<len;i++){
for(int j = i+1;j<len;j++){
res = prices[j]-prices[i];
max = Math.max(res,max);
}
}
return max;
}
}
时间复杂度过高,考虑使用动态规划降低时间复杂度。
状态定义:设dp
数组中dp[i]
表示到第i
天所有可行交易中利润的最大值,即前i
天利润最大值。
转移方程:分析得前i
天利润最大值为前i-1
天最大利润与第i
天卖出利润二者较大者。
d
p
[
i
]
=
m
a
x
{
d
p
[
i
−
1
]
,
p
r
i
c
e
s
[
i
]
−
m
i
n
(
p
r
i
c
e
s
[
1
:
i
)
]
}
dp[i]=max\{dp[i-1],prices[i]-min(prices[1:i)]\}
dp[i]=max{dp[i−1],prices[i]−min(prices[1:i)]}
前 i 日 最 大 利 润 = m a x ( 前 ( i − 1 ) 日 最 大 利 润 , 第 i 日 价 格 − 前 i 日 最 低 价 格 ) 前i日最大利润=max(前(i−1)日最大利润,第i日价格−前i日最低价格) 前i日最大利润=max(前(i−1)日最大利润,第i日价格−前i日最低价格)
其中得到前i日最低价得时间复杂度为O(i)
;可以在遍历数组时用一个变量保存即可。
再次降低时间复杂度O(i)到O(1),即只用遍历一遍数组即可得到转移方程:
d
p
[
i
]
=
m
a
x
{
d
p
[
i
−
1
]
,
p
r
i
c
e
s
[
i
]
−
m
i
n
(
m
i
n
P
r
i
c
e
,
p
e
i
c
e
s
[
i
]
)
}
dp[i]=max\{dp[i-1],prices[i]-min(minPrice,peices[i])\}
dp[i]=max{dp[i−1],prices[i]−min(minPrice,peices[i])}
降低空间复杂度:由于只用找到dp中的最大值,因此可用一个变量代替dp数组
p
r
o
f
i
t
=
m
a
x
(
p
r
o
f
i
t
,
p
r
i
c
e
s
[
i
]
−
m
i
n
(
m
i
n
P
r
i
c
e
s
,
p
r
i
c
e
s
[
i
]
)
)
profit = max(profit,prices[i]-min(minPrices,prices[i]))
profit=max(profit,prices[i]−min(minPrices,prices[i]))
最后时间复杂度为O(n),空间复杂度为O(1)
class Solution {
public int maxProfit(int[] prices) {
if(prices.length==0){
return 0 ;
}
int len = prices.length;
int res = prices[0];
int max = 0;
for(int i = 1;i<len;i++){
//记录前i日最小值
if(res>=prices[i]){
res = prices[i];
}
//更新最大利润
max = Math.max(max,prices[i]-res);
}
return max;
}
}
3 力扣地址
https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/58vmds/