题目:给出一个数组代表围柱的高度,求能围柱的最大的水量,例如数组{ 5,2,3,2,4 },最大水量为5。
如下图:黄色部分为围柱,绿色部分是能够围住的水,图中围柱的高度依次为 5,2,3,2,4最多能围住的水量是5。
思路:求出每个柱子上面能够存多少水,然后将每根柱子的存水量相加便能得到总的存水量,为求出每根柱子上能够存多少水,就要求出每根柱子左边最高的和右边最高柱子,然后用两者的最小值减去当前柱子的高度。 例如图中从左到右第二根柱子的高度为2,它左边最高柱子的值为5,右边最高柱子的值为4,因此它的最大存水量为 Min(4,5)-2=2。
解法1
利用上面思路,从左到右遍历每根柱子,遍历的时候求出每根柱子左边最高和右边最高柱子的值,然后利用s两者的最小值减去当前柱子的高度就行了。时间复杂度O(n^2),空间复杂度O(1)。
1 | public static int water1(int []arr) |
注意:
① 如果当前柱子大于它左右最大值的任何一个是存不了水的。
解法2
分析上面的算法发现算法的时间复杂度为O(n^2)的原因是对于每个元素都要从左到右,和从右到最左遍历其两边最大值,假如使用两个数组 left[ ] , right[ ]来保存每个元素左边最大值,右边最大值的话,这样就不用每次都遍历了,因此时间复杂度可以减少到O(n),但空间复杂度为O(n),典型的空间换时间算法。如下图:对于数组{ 5, 2 , 6 , 2 , 4 }它的左右数组如下
左数组:
右数组
1 | public static int water1(int []arr) |
上面算法的流程:
①从左到右遍历一次求出每个元素左边的最大值,保存在 left 数组中。
②从右到左遍历一次求出每个元素右边的最大值,保存在right数组中。
③最后一次遍历求出每个元素(每根柱子)的存水量。
改进解法2 :分析上面算法发现其实没有必要使用 left 数组,因为当从左到右遍历求存水量的过程中可以利用一个变量来保存当前元素左边的最大值。代码如下
1 | public static int water1(int []arr) |
解法3
能不能在时间复杂度O(n),空间复杂度O(1)的情况下来完成存水量的问题,答案是肯定的,用几幅图来描述一下解法3的过程。数组为 { 5,2,3,2,4 }。
①上面左右两边的黄色块分别表示当前元素左边最大值和右边最大值。
②left ,right分别代表从左到右移动和从右到左移动的指针。
③如果当前元素的左边最大值比右边最大值小,则left指针向右移动,否则right指针向左移动。
④这种左右指针移动的目的是为了保证所求的左右最大值一定是当前元素的左右最大值。
1 | public static int water3(int []arr) |