关于《盛水最多的容器》问题的思考
在力扣 #11 问题里,我们可能会陷入这样的误区:装水的容器内部(即两侧的挡板之间)不能存在一个高于水平面的挡板。其实应该理解为,选中两块板,然后拆掉中间的所有板,求能放入由两者、\(X\) 轴所限定区域的最大矩形面积。
这个面积的表示可以用数学符号来表示,下面我们将这个问题数学符号化。设每个挡板的高度为 \(H_n\),其中 \(0 \leqslant n \leqslant N-1\),\(N\) 是挡板的数量。任意两块挡板,设序号为 \(a\) 和 \(b\) 且满足 \(0 \leqslant a \leq b \leqslant N-1\),则由这两块挡板和 \(X\) 轴限定的最大矩形面积可以表示为:
于是,问题转换为:设 \(0 \leqslant a \lt b \leqslant N-1\) ,求 \(area\) 最大值。
刚看到这个问题的时候,我们很难不考虑遍历所有的可能性。这个方法一定可以找出最终的结果,但是却显得非常不优雅且低效。假设我们从 \(0\) 号挡板开始判断,那么每一个挡板都需要依次与编号比它大的所有挡板进行计算比较。所以可以用数学符号表示每一个挡板的比较次数 \(C_n\) 为:
也就是:
即:把单次计算和比较看作一次原子操作,那么整个遍历过程的时间复杂度为 \(\mathrm{O}(N^2)\)。显然这样的方式不是我们想要的,我们需要思考一种复杂度更低的方式。
不妨考虑这样一种情况,如果我们已经知道最大值就在某个范围内,如何选择下一个值来进行比较?比如我们在某一次判断过程中,已知 \(H_a \lt H_b\) 。那么下一步就要判断需要判定 \([a,b-1]\) 还是 \([a+1,b]\) 了。
方案一,考虑 \([a,b-1]\),此时分两种情况:
情况 A,若 \(H_{b-1} \leqslant H_b\):
情况 B,若 \(H_{b-1} \gt H_b\):
可见,从 \(b\) 位置的挡板切换到 \(b-1\) 时,一定有 \(area' < area\)。
方案二,考虑 \([a+1,b]\),此时分两种情况:
情况 A,若 \(H_a \geqslant H_{a+1}\):
情况 B,若 \(H_{a} \lt H_{a+1}\):
可见:
- 从较短的一边 \(a\) 位置的挡板切换到 \(a+1\) 时,如果 \(H_a \geqslant H_{a+1}\) ,则 \(area' \lt area\) 一定成立;
- 而 \(H_a \lt H_{a+1}\) 时可能出现 \(area' > area\) 的情况。
同理,若 \(H_a \geqslant H_b\),上述结论仍然成立。
综上,如果已知最大值在范围 \([a,b]\),那么想要找到下一个潜在的更大的值,必须要将两端较短的一个向着另一边移动。在这个理论之下,我们可以摒弃很多无用的检查,可以一直重复上述移动过程直到最终两个端点挨到一起。这样一来,每一个高度值只会被判断一次,整体的时间复杂度就是 \(\mathrm O(N)\)。