先讲述题目描述:
给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
暴力穷举肯定不行太无脑肯定会超时!想了一段时间,因为设i次卖票的最大利润取决于前面的买卖票策略,不断循环可以得到动态的路线并取得最后那个需要的最大利润值。
讲解下代码:
首先只定义两个状态:0(不持有票),1(持有票)。这边设置这两个状态刚好就够了,要是多去设置额外的状态比如第几天买了票第几天又卖了票的话,那他们的时间线前后买卖票的状态又要去额外设置比较麻烦。
定义一个二维数组(每行是第几天,每列的index也就是0,1就是代表上面的状态)
解释dp[i][0](某一天的不持有票的能获取的最大利润): 这个取决于两个状态,一个是来源于前面也就是dp[i-1][0]或者dp[i-1][1]+prices[i],前一个意思是继续不持有票嘛所以还是上一天继续不持有票能获得的最大利润,另一个是前一个已经卖了票的状态时的负利润加当前价格(这里是加是因为已经持有票了只能卖出去了,就加上票的纯利润也就是prices[i]),然后取这两个的最大值。
dp[i][1]的取值原因也可以自己推出了,这边需要有点动态规划的基础也是最难理解的部分。
class Solution {
public int maxProfit(int[] prices) {
int len = prices.length;
if(len<2){
return 0;
}
int[][] dp = new int[len][2];
//动态规划数组初始化,第一天的0也即是不持有状态的话也就是没收入嘛,那就是0咯,
dp[0][0]=0;
//第一天的第二种状态,1,也几就是持有票的话,那就是-prices[i],这边当作业务逻辑来看,也就是买这张票的话那纯利润就是负的,买票肯定是发费钱嘛
dp[0][1]=-prices[0];
for(int i=1;i<len;i++){
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
dp[i][1] = Math.max(dp[i-1][1],-prices[i]);
}
return dp[len-1][0];
}
}
结果还是比较耗时哈哈(时间复杂度O(N)),空间复杂度为O(2N)
但我继续想能不能还去优化浓缩代码,发现这个场景可以浓缩成更简单的数组题,从左往右获取数组里右边偏向更大值减去偏向左边更小值的值,因为只能先买再卖票嘛,很像升序两数来用后面数减去前面数,假如你要是用[7,1]里的7-1的话就代表了你在买票前把票卖了,不符合逻辑。
所以我用minPrice来表示数组不断遍历时的当前最小值,
然后用maxPro不断比较自己与当前值减去最小值,要是这个差值更他就当新的maxPro。
这样把代码以及逻辑简化了不少
class Solution {
public int maxProfit(int[] prices) {
int len = prices.length;
if(len<2){
return 0;
}
int maxPro = 0;
int minPrice = prices[0];
for(int i=1;i<len;i++){
maxPro = Math.max(maxPro,prices[i]-minPrice);
if(prices[i]<minPrice)
{
minPrice = prices[i];
}
}
return maxPro;
}
}
运行结果如下:
时间也缩短到2ms了嘻嘻,关键也没创建新的数组来额外开销。