Best Time to Buy and Sell Stock III
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most two transactions.
现在A股涨这么好,要是股票都提前知道价格就好了(@[email protected])
首先,考虑如果至多只能做一次交易呢?
那很简单嘛,大家都知道O(n^2)的方法,两个循环,确定(buyDay, sellDay)取到最大的profit。
怎么优化呢?
其实如果只做一次交易,那么遍历一次数组就可以了。
从0开始,到最后一天。在遍历到第i天的时候,我们用prices[i]减去当前所知道的最小的price,看一下是否比当前max profit大,如果是,就更新max profit。
我们不用关心后面的更小值哦,因为它们不影响当前profit,毕竟你只有先买了才能卖。
同理也可以从最后一天开始往前遍历,这时候我们不记录当前所知道的最小price,而是最大值,用最大值减去prices[i]来和max profit比较。代码在这下面。
public int maxProfit(int[] prices) { if (prices.length <= 1) { return 0; } int maxProfit = 0; int minIndex = 0; for(int i = 1; i < prices.length; i++) { if (prices[i] < prices[minIndex]) { minIndex = i; } if (prices[i] - prices[minIndex] > maxProfit) { maxProfit = prices[i] - prices[minIndex]; } } return maxProfit; }
然后我们来看如何计算”买卖两次“的最大profit。
因为有了买卖一次的交易算法,我们比较容易去这样想。把整个个prices数组分成两部分,计算前一部分买卖一次的最大值,计算后一部分买卖的最大值,然后求和。然后从0到length重复该操作,求出整个数组上买卖两次的最大值。
不过这样复杂度变成了O(n^2)。
有没有更好的方法呢?
其实这样想,我们在计算买卖一次的遍历过程中,已经有这样的信息了,那就是,在第0天到第i天,买卖一次能得到的最大profit,假设是forward[i]。同理,如果从后面往前遍历的过程中,我们拿到从第i天到最后一天,买卖一次能得到的最大profit,假设是backward[i]。
于是我们最后一步要求的不就是max(forward[i] + backward[i] for i = 0... length)嘛?这样O(n)就能求出来了。
代码如下:
public int maxProfit(int[] prices) { int[] maxProfit = new int[prices.length]; if (prices.length <= 1) { return 0; } //forward int minIndex = 0; for(int i = 1; i < prices.length; i++) { if (prices[i] < prices[minIndex]) { minIndex = i; } maxProfit[i] = Math.max(maxProfit[i], prices[i] - prices[minIndex]); } //backward int maxIndex = prices.length - 1; int ret = 0; int iMax = 0; for(int i = prices.length - 2; i >= 0; i--) { if (prices[i] > prices[maxIndex]) { maxIndex = i; } iMax = Math.max(iMax, prices[maxIndex] - prices[i]); ret = Math.max(ret, iMax + maxProfit[i]); } return ret; }
注意我们没有使用backward[i],因为第二次遍历直接就能得到max profit了。