leetcode-11 Container With Most Water

Given n non-negative integers a1; a2; ...; an, where each represents a point at coordinate (i; ai). n
vertical lines are drawn such that the two endpoints of line i is at (i; ai) and (i; 0). Find two lines, which
together with x-axis forms a container, such that the container contains the most water.

http://www.cnblogs.com/TenosDoIt/p/3812880.html


容器大小取决与短板大小,整个体积为min(a[i], a[j])*(j-i)
1.暴力 O(n*n) 会超时
    可以直接暴力循环,中间可以剪枝,根据当前最大面积可以求得最小开始的底边
2.预处理每个挡板的左边最高和右边最高,这样蓄水区间就可知道O(nlog(n))

    假设f[i] 表示数组vec[i,i+1,…]内所有height按照原来的位置顺序排列好以后的最大水量

    那么f[i-1]可以在O(1)时间内计算出来:因为vec[i-1].height 小于vec[i,i+1,…]内的所有height,所以以vec[i-1].index为边界的容器高度为vec[i-1].height,最大水量只需要分别计算vec[i,i+1,…]内按原始位置排列最前面和最后面的height,取两者的较大值。即下图中,黑色是最低的,要计算以黑色为边界的容器的最大水量,只需要分别和第一个和最后一个计算,去两者较大值。


3.双指针,O(n) 时间和 O(1) 空间
    时间复杂度O(n),两个指针i, j分别从前后向中间移动,两个指针分别表示容器的左右边界。每次迭代用当前的容量更新最大容量,然后把高度小的边界对应的指针往中间移动一位。
    正确性证明:由于水的容量是有较小的那个边界决定的,因此某次迭代中,假设height[i] < height[j],那么j 减小肯定不会使水的容量增大,只有i 增加才有可能使水的容量增大。但是会不会有这种可能:当前的i 和 某个k (k > j)是最大容量, 这也是不可能的,因为按照我们的移动规则,既然右指针从k 移动到了j,说明i 的左边一定存在一个边界 m,使m > k,那么[m, k]的容量肯定大于[i, k],所以[i,k]不可能是最大容量。
//
// start: find max area with two index                                               
/************************************************************************/
/* method1_1: BruteForce                                                                  */
/************************************************************************/
int maxArea_BruteForce1(vector<int>& height)
{
        int result = 0, len = height.size();
        if(len < 2)
            return 0;

        for(int i = 0; i < len-1; i++)
        {
            for(int j = i+1; j < n; j++)
            {
                int tmp = min(height[i], height[j])*(j-i);
                if(result < tmp)
                    result = tmp;
            }
        }
        return result;
}

/************************************************************************/
/* method1_2:  BruteForce with pruning                                           */
/************************************************************************/
int maxArea_BruteForce1(vector<int>& height)
{
        int result = 0, len = height.size();
        if(len < 2)
            return 0;

        for(int i = 0; i < len-1; i++)
        {
            if(height[i] == 0)
                continue;
            for(int j = max(i+1, result/height[i]+i); j < n; j++)
            {
                int tmp = min(height[i], height[j])*(j-i);
                if(result < tmp)
                    result = tmp;
            }
        }
        return result;
}

/**************************************************************************/
/* method2:  sort with each height and find max len of delta index */
/**************************************************************************/
struct   Node
     {
         int   height;
         int   index;
         Node( int   h,  int   i):height(h),index(i){}
         Node(){}
         bool   operator < ( const   Node &a) const  // 利用sort排序
         {
             return   height < a.height;
         }
     };

int maxArea_Sort(vector<int>& height)
{
        int result = 0, len = height.size();
        if(len < 2)
            return 0;

        vector<Node>Nodes(len);
        for(int i = 0; i < len-1; i++)
        {   
            Nodes[i].index = i;
            Nodes[i].height = height[i];
        }
        sort(Nodes[i].begin(), Nodes[i].end());  // height降序排序

        int start = Nodes[n-1].index, end = start;  // 记录值较大集合中下标最大及最小
        for(int i = n-2; i >= 0; i--) 
        {
            start = min(start, Nodes[i].index);
            end = max(end, Nodes[i].index);
            result = max( result, Nodes[i].height * max((Nodes[i].index-start), end-Nodes[i].index) );
        }
        return result;
}

/*****************************************************************************************************/
/* method3:  left and right index moving towards middle, each time moving less value one */
/*****************************************************************************************************/
int maxArea_MoveLeftRightPointer(vector<int>& height)
{
        int result = 0, len = height.size();
        if(len < 2)
            return 0;
        int left = 0, right = n - 1;
        while(left < right)
        {
            result = max(result, min(height[left], height[high])*(right-left));
            if(height[left] < height[right])
                left++;
            else
                right++;
        }
        return result;
}
// end
//



近似问题: 柱子积水问题  leetcode-42

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.


方法1:

分析某个柱子,发现该柱子上水的高度和其左右两边的最高柱子有关,设该柱子左边所有柱子中最高的为leftmax,右边所有柱子中最高的为rightmax,如果min(leftmax, rightmax) 大于该柱子的高度,那么该柱子上可以蓄水为min(leftmax, rightmax) - 该柱子高度,如果min(leftmax, rightmax) <= 该柱子高度,则该柱子上没有蓄水。

可以从后往前扫描一遍数组求得某个柱子右边的最高柱子,从前往后扫描一遍数组求得某个柱子左边的最高柱子, 然后按照上面的分析可以求得所有的蓄水量。


方法2:

可以换一种思路,如下图,如果我们求出了两个红色框的面积,然后再减去框内黑色柱子的面积,就是水的面积了时间复杂度O(N),空间O(1), 数组扫描2次


如何求红色框内的面积呢,我们先求出最大的柱子高度,然后左右分开求。求面积是是一层一层的累加。


方法3:
同方法2的思想,利用上面左右指针的方法, 只需扫描一遍数组。

// start: cal trapping water with int*                                               
/**************************************************************************************/
/* method1:  find LeftMax and RightMax to cal trapping water for each index */
/* O(n) scan for twice, space: O(n)                                                                         */
/**************************************************************************************/

int trap_Scan(int A[], int n)
{
    int result = 0;
    if(n < 3)
        return 0;
    vector<int> RightMax(n);  // 当前柱子右边的最大高度 = 当前柱子右一的右边最大高度与右一高度的最大值
    int Max = 0; 
    for(int i = n-1; i >= 0; i--)
    {
        RightMax[i] = Max;
        Max = max(Max, A[i]);
    }
    Max = 0;
    for(int i = 0; i < n; i++)
    {
        int deltaHeight = min(Max, RightMax[i]);
        if(deltaHeight > A[i])  // 左右最大高度较小值比当前值要大时才会有积水
        {
            result += deltaHeight - A[i];
        }
        Max = max(Max, A[i]);  // 此时直接用Max记录下一个柱子左边的最大高度,而不用再用数组记录
    }
    return result;
}

/**************************************************************************************/
/* method2:  find water area left and right of the highest index                         */
/* O(n) scan for twice, space: O(1)                                                                         */
/**************************************************************************************/
int trap_Area(int A[], int n)
{
    if(n < 3)
        return 0;
    int maxHeight = 0, maxIdx = 0;  // 记录最高
    for(int i = 0; i < n; i++)
    {
        if(A[i] > maxHeight)
        {
            maxHeight = A[i];
            maxIdx = i;
        }
    }
    int height = 0;
    int totalArea = 0;  // 围成的所有面积
    int valArea = 0;// 有值的面积
    for(int i = 0; i < maxIdx; i++)  // 先从左向右,找到第一个当前最大的值
    {
        if(A[i] > height)  // 第一次遇到本层最高,用total记录总面积,遍历每一个idx去记录有值的面积
        {
            totalArea += (maxIdx-i)*(A[i]-height);  // 面积的高度不是1,而是当前层与前一层之间的差
            height = A[i];  //始终记录从开始到当前的最大高度
        }
        valArea += A[i];
    }

    height = 0;
    for(int i = n-1; i > maxIdx; i--)  // 先从右向左,找到第一个当前最大的值
    {
        if(A[i] > height)  // 第一次遇到本层最高,用total记录总面积,遍历每一个idx去记录有值的面积
        {
            totalArea += (i-maxIdx)*(A[i]-height);  // 面积的高度不是1,而是当前层与前一层之间的差
            height = A[i];  //始终记录从开始到当前的最大高度
        }
        valArea += A[i];
    }
    return totalArea - valArea;
}

/*****************************************************************************************************/
/* method3:  left and right index moving towards middle, each time moving less value one */
/*****************************************************************************************************/
int trap_MoveLeftRightPointer(int A[], int n)
{
    if(n < 3)
        return 0;
    int totalArea = 0;  // 围成的所有面积
    int valArea = 0;  // 有值的面积
    int height = 0;  // 记录当前处理的层
    int left = 0, right = n - 1;
    while(left < right)
    {
        if(A[left] < A[right])  // 在左侧移动指针
        {
            if(A[left] > height)  // 需要累加当前层
            {
                totalArea += (A[left]-height)*(right-left+1);
                height = A[left];
            }
            valArea += A[left];
            left++;
        }
        else
        {
            if(A[right] > height)
            {
                totalArea += (A[right]-height)*(right-left+1);
                height = A[right];
            }
        }
        valArea += A[right];
        right--;
    }
    return totalArea - valArea;
}
// end



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值