Trapping Rain Water
Given n non-negative integers representing an elevation map where the width of each bar is 1,compute how much water it is able to trap after raining.
For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1],return 6.
思路:每个数代表了柱子的高度,求总的成水量。最初的思路是这样的:首先找到最高的柱子,然后找第二高的柱子,它们之间的成水量,就是第二高的柱子的高度,乘以它们之间的距离,然后减去它们中间的所有柱子的面积(高度)。这两根柱子就形成了一个边界,然后依次去找第三高,第四高...的柱子,分别求出它们与边界之间的成水量即可。
既然要从大到小寻找柱子,那么就得排序,按照高度从大到小排序,主要是记录这些柱子的索引。代码如下:
typedef struct { int value; int index; }Node; //辅助结构,记录高度和索引,按照高度从大到小排序 #define min(x,y) (((x)<(y))?(x):(y)) void swapnode(Node*n1, Node *n2) { Node tmp; tmp.value = n1->value; tmp.index = n1->index; n1->value = n2->value; n1->index = n2->index; n2->value = tmp.value; n2->index = tmp.index; } //下面是对Node结构的快速排序,从大到小。 int partition(Node*nset, int left, int right) { int key = nset[right].value; int i = left-1; int j; for(j = left; j <right; j++) { if(nset[j].value >key) { i++; swapnode(nset+i,nset+j); } } swapnode(nset+i+1,nset+right); return i+1; } void qsortnode(Node*nset, int left, int right) { int q = -1; if(left < right) { q = partition(nset,left, right); qsortnode(nset, left,q-1); qsortnode(nset, q+1,right); } } //主函数 int trap(int*height, int heightSize) { if(heightSize <= 2)return0; Node *vi = calloc(heightSize,sizeof(Node)); int i; int sum = 0; int left, right, minvalue; //首先对辅助结构进行排序,主要是得到他们的索引顺序 for(i = 0; i <heightSize; i++) { vi[i].value = height[i]; vi[i].index = i; } qsortnode(vi, 0, heightSize-1); //以最高的柱子为边界 left = right = vi[0].index; sum = 0; for(i = 1; i <heightSize; i++) { int index = vi[i].index; int value = vi[i].value; /*如果该柱子已经处于边界中,则需减去他们的面积,因为在得到边界的时候,已经将这些柱子计算在内了。*/ if(index < right &&index > left) { sum -= value; } /*扩展左边界,计算边界内的盛水总面积,包括中间柱子的面积*/ else if(index <left) { sum += height[index]* (left-index-1); left = index; } /*扩展右边界,计算边界内的盛水总面积,包括中间柱子的面积*/ else if(index >right) { sum += height[index]* (index-right-1); right = index; } } return sum; }
上面的算法不是最优的,时间复杂度为O(nlogn),空间复杂度为O(n)。leetcode上的测试时间是8ms。时间和空间主要浪费在排序上了,其实可以不用排序的。思路如下:
首先找到最高的柱子,以它为隔板,将整个数组分成左右两部分。左半部分中,从左到右依次扫描,因为隔板的存在,所以只要左边的柱子比右边高,则高度差就可以盛水。右半部分同理。这种算法的时间复杂度为O(n),空间复杂度为O(1)。leetcode上的测试时间是4ms。代码如下:
int trap_nb(int*height, int heightSize) { int i; int sum = 0; int maxindex = 0; int prehight = 0; //找最高柱子的索引 for(i = 1; i <heightSize; i++) { if(height[i] >height[maxindex]) { maxindex = i; } } //从左到右扫描,计算高度差 prehight = 0; for(i = 0; i <maxindex; i++) { if(height[i] >prehight) { prehight = height[i]; } sum += prehight - height[i]; } //从右到左扫描,计算高度差 prehight = 0; for(i = heightSize-1;i > maxindex; i--) { if(height[i]> prehight) { prehight = height[i]; } sum += prehight - height[i]; } return sum; }
参考: https://github.com/haoel/leetcode/blob/master/algorithms/trappingRainWater/trappingRainWater.cpp