接雨水问题(官方的解法三:双指针)

要求:

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例:

输入: [0,1,0,2,1,0,1,3,2,1,2,1]

输出: 6

思路:

其实就是按列求值。关注当前列,其左边最高的墙,右边最高的墙。并关注左右两边最高墙的较矮的墙的高度。

  • 较矮墙的高度高于当前列,存的水量为(较矮的墙的高度-当前列的高度)
  • 较矮墙的高度等于当前列,存的水量为0
  • 较矮墙的高度小于当前列,存的水量为0
  • 另外,两端是肯定存不住水的

publicinttrap(int[] height){

int sum = 0;

//两端不用考虑

for (int i = 1; i < height.length - 2; i++) {

int max_left = 0;

//找到左边最高墙,从当前列从右往左遍历

for (int j = i-1; j >= 0; j--) {

if (height[j] > max_left) {

max_left = height[j];

}

}

int max_right = 0;

//找出右边最高墙,从当前列从左往右遍历

for (int j = i+1; j < height.length; j++) {

if (height[j] > max_right) {

max_right = height[j];

}

}

//比较两端最高墙,找出较矮的墙

int min = Math.min(max_left, max_right);

if (min > height[i]) {

sum += (min - height[i]);

}

}

return sum;

}

动态规划进行优化

用max_left[i]代表第i列左边最高墙的高度,max_right[i]代表第i列右边最高墙的高度。实际上,max_left[i]=max(max_left[i-1], height[i-1]), max_right[i] = max(max_right[i+1], height[i+1])

这样就不用每次都要在循环中重新求一次max_left和max_right了。但依旧存在缺点,就是需要从左向右遍历,还是需要数组来存储左右两边的最大墙。

publicinttrap(int[] height){

int sum = 0;

int[] max_left = newint[height.length];

int[] max_right = newint[height.length];

for (int i = 1; i < height.length-1; i++) {

max_left[i] = Math.max(max_left[i-1], height[i-1]);

}

for (int i = height.length-2; i >= 0; i--) {

max_right[i] = Math.max(max_right[i+1], height[i+1]);

}

for (int i = 1; i < height.length - 1; i++) {

int min = Math.min(max_left[i], max_right[i]);

if (min > height[i]) {

sum += (min-height[i]);

}

}

return sum;

}

双指针的改进:

在本题中,max_left[i]和max_right[i]其实都只使用过一次,所以可以使用双指针交替前进,避免数组的使用。已知max_left = Math.max(max_left, height[i-1]),即height[left-1]是可能成为max_left的,所以只要height[left-1]<height[right+1],那么max_left就一定是小于max_right的。

此处,left代表左侧指针的索引,right代表右侧指针的索引。只要max_left<max_right,就从左往右更新,因为较矮的墙会在左侧更新,否则就从右往左更新。

publicinttrap(int[] height){

int sum = 0;

int max_left = 0;

int max_right = 0;

int left = 1;//左指针

int right = height.length - 2;//右指针

for (int i = 1; i < height.length - 1; i++) {

//从左到右更新

if (height[left-1] < height[right+1]) {

max_left = Math.max(max_left, height[left-1]);

int min = max_left;

if (min > height[left]) {

sum += (min-height[left]);

}

left++;

} else {//从右往左更新

max_right = Math.max(max_right, height[right+1]);

int min = max_right;

if (min > height[right]) {

sum += (min - height[right]);

}

right--;

}

}

return sum;

}

以上是 接雨水问题(官方的解法三:双指针) 的全部内容, 来源链接: utcz.com/a/28154.html

回到顶部