leetcodeFace63 股票的最大利润 传统解法到峰谷法的演进过程
很多清奇的解法不是靠拍脑袋一蹴而就的,而是随着传统解法一步一步优化来的。刷题时不需要经常焦虑自己没有创新毒辣的解题思路,应该从传统解法开始,考虑当前解法有什么可优化的点,并去实现它们。
最容易想到的自然是暴力法,我们假设每个元素都可以作为买入元素的前提下,再假设在其之后的每个元素尝试卖出,纪录最大差值即可:
public final int maxProfit(int[] prices) { int re = 0; int length = prices.length; for (int i = 0; i < length - 1; i++) { for (int j = i + 1; j < length; j++) { int temp = prices[j] - prices[i]; if (temp > 0) { re = Math.max(re, temp); } } } return re; }
然后我们想办法对问题进行分治,找到暴力法中可重复使用的计算部分进行缓存,避免重复计算。问题规模由 prices 的长度决定,我们按照其长度分割问题。G(flag) 为前 flag 天可得最大利润,则状态转移方程为 G(flag) = Max { G(flag-1) , prices[flag]-min(prices[0]...prices[flag]) }:
/** * @Author Nxy * @Date 2020/5/10 23:40 * @Description G(flag) 为前 flag 天可得最大利润 * G(flag)= Max { G(flag-1) , prices[flag]-min(prices[0]...prices[flag]) } * 我们用一个两元素数组做为返回值,第一个元素为 prices 在 0-flag 最小值,第二个元素为最大利润 */ public final int maxProfitDP(int[] prices) { if (prices == null || prices.length == 0) { return 0; } int[][] cache = new int[prices.length + 1][2]; int[] re = maxProfitDP(prices, prices.length - 1, cache); return re[1]; } public final int[] maxProfitDP(int[] prices, int flag, int[][] cache) { if (flag == 0) { return new int[]{prices[0], 0}; } if (cache[flag][1] != 0) { return cache[flag]; } int[] beforePrice = maxProfitDP(prices, flag - 1, cache); int newMinCost = Math.min(beforePrice[0], prices[flag]); int nowPrice = prices[flag] - newMinCost; int[] returnNums = new int[2]; returnNums[0] = newMinCost; returnNums[1] = Math.max(beforePrice[1], nowPrice); cache[flag] = returnNums; return returnNums; }
将带缓存的分治转化为递推表示,得到 DP 解法:
public final int maxPricesDP2(int[] prices) { int length = prices.length; int[][] cache = new int[length][2]; cache[0] = new int[]{prices[0], 0}; for (int i = 1; i < length; i++) { int newMinCost = Math.min(cache[i - 1][0], prices[i]); int maxPrice = Math.max(cache[i - 1][1], prices[i] - newMinCost); cache[i] = new int[]{newMinCost, maxPrice}; } return cache[length - 1][1]; }
从 DP 我们看到,其实没必要用缓存数组,只需要两个变量分别记录前 flag 个元素中的最小值和前 flag-1 个元素的最大利润即可。这样我们得到了峰谷法解法:
public final int maxPrices(int[] prices) { int length = prices.length; if (length == 0) { return 0; } int minCost = prices[0]; int maxPrices = 0; for (int i = 1; i < length; i++) { minCost = Math.min(minCost, prices[i]); maxPrices = Math.max(maxPrices, prices[i] - minCost); } return maxPrices; }
当你看清人们的真相,于是你知道了,你可以忍受孤独