Best Time to Buy and Sell Stock(有一个数组,数组中第i个元素表示第i天的股票,如果你只有一次交易机会,买一次卖出一次,就出最大利润。)
思路:动态规划法。从前向后遍历数组,记录当前出现过的最低价格,作为买入价格,并计算以当天价格出售的收益,作为可能的最大收益,整个遍历过程中,出现过的最大收益就是所求。O(n)的时间复杂度,O(1)的空间复杂度。
主要代码:
min= Math.min(min,prices[i]);
maxProfit = Math.max(maxProfit,prices[i]-min);
Best Time to Buy and Sell Stock I(在上一题的基础上,条件改为可以进行多次交易,但是必须先买在卖)
思路:贪心法。从前往后遍历数组,只要当天的价格高于前一天的价格,就算入收益。O(n)的时间复杂度,O(1)的空间复杂度。
主要代码:int profit = prices[i]-prices[i-1]; if(profit>0) max += profit;Best Time to Buy and Sell Stock II(在上一题的基础上,条件改为最多只能进行两次交易)
思路:动态规划法。以第i天为例,计算第i天之前进行一次交易的最大收益preProfit[i],和第i天之后进行一次交易的postProfit[i],遍历一遍之后求max(preProfit[i]+postProfit[i])为最大收益。其中求preProfit[i]和postProfit[i]的方法和I的一样。O(n)的时间复杂度,O(n)的空间复杂度。需要额外的两个数组preProfit和postProfit来保存对应的利润。
Best Time to Buy and Sell Stock III (条件改为至少K次交易)(此题转载别人的解析)
思路:特殊动态规划法。传统的动态规划我们会这样想,到第i天时进行j次交易的最大收益,要么等于到第i-1天时进行j次交易的最大收益(第i天价格低于第i-1天的价格),要么等于到第i-1天时进行j-1次交易,然后第i天进行一次交易(第i天价格高于第i-1天价格时)。于是得到动规方程如下(其中diff = prices[i] – prices[i – 1]):
profit[i][j] = max(profit[i – 1][j], profit[i – 1][j – 1] + diff)看起来很有道理,但其实不对,为什么不对呢?因为diff是第i天和第i-1天的差额收益,如果第i-1天当天本身也有交易呢(也就是说第i-1天刚卖出了股票,然后又买入等到第i天再卖出),那么这两次交易就可以合为一次交易,这样profit[i – 1][j – 1] + diff实际上只进行了j-1次交易,而不是最多可以的j次,这样得到的最大收益就小了。
那么怎样计算第i天进行交易的情况的最大收益,才会避免少计算一次交易呢?我们用一个局部最优解和全局最有解表示到第i天进行j次的收益,这就是该动态规划的特殊之处。
用local[i][j]表示到达第i天时,最多进行j次交易的局部最优解;用global[i][j]表示到达第i天时,最多进行j次的全局最优解。它们二者的关系如下(其中diff = prices[i] – prices[i – 1]):
local[i][j] = max(global[i – 1][j – 1] , local[i – 1][j] + diff)
global[i][j] = max(global[i – 1][j], local[i][j])local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生,当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff;当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况:如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j];如果第i天有交易(卖出),那么global[i][j]=local[i][j]。
参考:http://www.cnblogs.com/grandyang/p/4295761.html代码:时间O(n),空间O(n)
int days = prices.length; if (k >= days) return maxProfit2(prices); int[][] local = new int[days][k + 1]; int[][] global = new int[days][k + 1]; for (int i = 1; i < days ; i++) { int diff = prices[i] - prices[i - 1]; for (int j = 1; j <= k; j++) { local[i][j] = Math.max(global[i - 1][j - 1], local[i - 1][j] + diff); global[i][j] = Math.max(global[i - 1][j], local[i][j]); } } return global[days - 1][k];
动规所用的二维辅助数组可以降为一维的,即只用大小为k的一维数组记录到达第i天时的局部最优解和全局最优解。需要注意的是,由于第i天时交易k次的最优解依赖于第i-1天时交易k-1次的最优解,所以数组更新应当从后往前(即从k到1)更新。
Best Time to Buy and Sell Stock with Cooldown (可以交易多次,但是在每次在卖出股票后必须隔一天才能再买股票,有一天的冷冻期)
思路:维护两个数组,数组buy表示买入的最大利润,它需要考虑第i天是否买入;数组sell表达卖出的最大利润,它需要考虑第i天是否卖出。然后写出下面的递推公式:
buy[i] = max(buy[i-1], sell[i-2] - prices[i])
sell[i] = max(sell[i-1], buy[i-1] + prices[i])其中buy[0] = -prices[0], buy[1] = max(-prices[0], -prices[1]), sell[0] = 0, sell[1] = max(0, prices[1] - prices[0])
比如:
prices:1, 2, 3, 0, 2
buy: -1, -1, -1, 1, 1
sell 0, 1, 2, 2, 3
public int maxProfit(int[] prices) {
if(prices == null || prices.length <= 1) {
return 0;
}
int len = prices.length;
int[] buy = new int[len];
int[] sell = new int[len];
buy[0] = -prices[0];
buy[1] = Math.max(-prices[0], -prices[1]);
sell[0] = 0;
sell[1] = Math.max(0, prices[1]-prices[0]);
for(int i = 2; i < len; i++) {
buy[i] = Math.max(buy[i-1], sell[i-2]-prices[i]);
sell[i] = Math.max(sell[i-1], buy[i-1]+prices[i]);
}
return sell[len-1];
}