有一道算法题,很有意思:给你一个数组,代表每天股票的买/卖价序列,要求在序列期间内最多只允许买、卖各一次(先买后卖,买要在卖之前),求此期间内的最大收益是多少(可以选择不卖,即收益为0)。举例如下:
- 价格序列为[7,1,5,3],则代表第一天股票价格为7,第二天为1,第三天为5,第四天为3(数组长度不定),此时第二天价格为1的时候买入,第三天价格为5的时候卖出,则赚取最大的收益4。如果到了第四天再卖,则只能收益2了。你也不能第一天卖,第二天买,虽然收益是6,但是没有买就卖不出去,只能先买后卖,所以这样是犯规的
- 价格序列为[7,1,5,3,6,4],则最大收益为5,即选择第2天买入(买入价1)、第5天卖出(卖出价6)
- 价格序列为[7,6,4,3,1],则最大收益率为0,即只能选择买入,但因为价格持续下降,所以无法卖出。这种持续走低的情况还不如不买,也就是收益为0
好了,题目就是这样,怎样计算最大的收益呢?如果用暴力破解,也就是O(n2)的时间复杂度,这样也可以算出来,但是要遍历两遍,相当不经济。有没有遍历一遍就能算出来的办法呢?有的,答案就是动态规划!动态规划能够把n平方的复杂度,简化为n的复杂度,着实厉害啊!
遍历到第一天的时候,我们记住价格,遍历到第二天的时候,我们可以得到前两天的最小值,遍历到第三天的时候,我们可以得到前三天的最小值……当我们遍历到N天的时候,我们可以得到前N天的最小值,然后和N+1天的价格相比,如果N+1天的价格小于等于前N天的最小值,那么肯定是不能卖的,那么就刷新最小值!如果N+1天的价格大于前N天的最小值,那么就计算前N天的最小值与N+1天的价格的差,也就是盈利差,并随时记录下来!如果遇到更大的,就不断刷新,就像一项记录一样,如果打破了就刷新最高值,没有打破就维持原来的最高值。当然盈利差初始化为0,这个也很好理解。算到最后一天,我们不仅得到了所有天数里面的最小价格,还得到了最大的盈利差,这个盈利差就是需要返回的答案!思路分析到这里,下面是实现的代码,供参考:
/**
* 算法:计算交易日股票最大收益
* @param prices 各交易日股价
* @author LiYang
* @return 最大的股票价差(收益)
*/
public static int maxStockProfit(int[] prices) {
// 交易日不足2日,则返回0
if (prices == null || prices.length == 0 || prices.length == 1) {
return 0;
}
// 初始设置历史最低价为第一天的股价
int lowestPriceInHistory = prices[0];
// 最大股票价差(收益)初始化为0
int maxProfit = 0;
// 从第二个交易日开始遍历计算
for (int i = 1; i < prices.length; i++) {
// 如果当前交易日股价,比历史最低价要高
if (prices[i] - lowestPriceInHistory > 0) {
// 更新最高的股票价差(收益)
maxProfit = Math.max(prices[i] - lowestPriceInHistory, maxProfit);
} else {
// 否则更新历史最低价
lowestPriceInHistory = prices[i];
}
}
// 返回最终计算的最大股票价差(收益)
return maxProfit;
}