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

原文地址:https://www.cnblogs.com/gqtcgq/p/7247137.html