问题来源于leetcode.42。
问题描述:
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
解题过程:
1、最开始,尝试了遍历寻找左右边界,然后累计体积的方式。
从左侧开始,依次进行遍历,判断该点左侧和右侧都有比自己当前值高的点,表示该点可以积水,然后进行单点体积计算,累加到结果量中。
public int Trap(int[] height)
{
int re = 0;
for (int i = 1; i < height.Length - 1; i++)
{
int mid = height[i];
int le = i, ri = i;
int tl = mid, tr = mid;
for (int j = i - 1; j >= 0; j--)
{
if (height[j] >= tl)
{
le = j;
tl = height[j];
}
else
{ };
}
for (int j = i + 1; j < height.Length; j++)
{
if (height[j] >= tr)
{
ri = j;
tr = height[j];
}
else
{ };
}
if (le != i && ri != i)
{
int h = Math.Min(height[le], height[ri]);
//for(int j=le+1;j<ri;j++)
{
re += (h - height[i] > 0) ? h - height[i] : 0;
}
}
}
return re;
}
但是这样基于单点左右查找,算法复杂度为O(n*n),当数组比较大时,值比较和嵌套遍历,都会比较耗时,不得已进行算法简化。
2、分水岭
仔细观察储水后状态,发现其实个凸形结构,像是个梯田。从左侧开始,逐级攀爬到封顶,然后是平顶,再从平顶逐级下落。所以把过程分为左攀爬,右攀爬,平顶三个过程,分别计算累加。
public int Trap(int[] height)
{
int re = 0;
if (height.Length < 3) return re;
int max = height.Max();
int fm = Array.IndexOf(height, max);
int lm = Array.LastIndexOf(height, max);
int tmp = 0;
for (int i = 0; i < fm; i++)
{
if (height[i] > tmp) tmp = height[i];
else if (height[i] < tmp)
{
re += tmp - height[i];
}
}
tmp = 0;
for (int i = height.Length - 1; i > lm; i--)
{
if (height[i] > tmp) tmp = height[i];
else if (height[i] < tmp)
{
re += tmp - height[i];
}
}
for (int i = fm; i < lm; i++)
{
re += max - height[i];
}
return re;
}