题目介绍
力扣121题:leetcode-cn.com/problems/be…
分析
计算机解决问题的方法就是穷举。遇到一个问题,如果想不到什么奇技淫巧,那么首先请读者自问:如何穷举这个问题的所有可能性?
这个问题的穷举很简单,我们代码可以这样写:
class Solution {
public int maxProfit(int[] prices) {
int max = 0;
for(int buy = 0; buy < prices.length; buy++) {
for(int sell = buy + 1; sell < prices.length; sell++) {
max = Math.max(max, prices[sell] - prices[buy]);
}
}
return max;
}
}
实际上,这个解法就是可行的,能够得到正确答案,但是力扣上提交会出现超时现象。我们分析一下这个算法在干嘛,就能发现一些冗余计算。
如上图,可以看到大量的重复操作。我们相当于固定了买入时间 buy,然后将 buy 后面的每一天作为 sell 进行穷举,只为寻找 prices[sell] 最大的那天,因为这样 prices[sell] - prices[buy] 的差价才会最大。
如果反过来想,固定卖出时间 sell,向前穷举买入时间 buy,寻找 prices[buy] 最小的那天,是不是也能达到相同的效果?是的,而且这种思路可以减少一个 for 循环。
public class Solution {
public int maxProfit(int prices[]) {
int res = 0;
int curMin = prices[0];
for (int sell = 1; sell < prices.length; sell++) {
curMin = Math.min(curMin, prices[sell]);
res = Math.max(res, prices[sell] - curMin);
}
return res;
}
}
为什么可以减少一个 for 循环呢?我举个例子你就很容易理解了。
假设你有一堆数字,你知道其中最大的数,现在从中取走一个数,你还知道最大的那个数是多少吗?不一定,如果拿走的这个数不是那个最大数,那么最大数不变;如果拿走的恰好是那个最大的数,你就得重新遍历这堆数字以寻找之前第二大的那个数,作为新的最大数。这就是我们的原始算法,每向后移动一位,就要重新遍历寻找最大值。
但是,假设你知道一堆数字中最小的那个,再添加一个新的数字,你现在是否知道最小的数字是那个?知道,只要比较一下新数和当前最小的数字,就能得到新的最小数。这就是优化算法的情况,所以可以消除嵌套循环的计算冗余。
关键不在于最大值还是最小值,而是数字的添加和减少。添加新数时,可以根据已有最值,推导出新的最值;而减少数字时,不一定能直接推出新的最值,不得不重新遍历。