如果两根柱子能接水?

问题描述:
给你n个非负整数a1, a2, …, an,每个数代表一个柱子的高度,如果两根柱子跟x轴能够构成一个容器,那么用这个容器接水.试问选择哪两根柱子能够使得所接的水最多?

在这里插入图片描述
算法思想:
遍历这个高度数组,
首先判断以某根柱子高度为所接水的高度的面积最大者,这个面积所对应的两个柱子(高度).判断过程是这样的:设计两个指针,固定一个指针指向当前的高度,将另一个指针遍历这个高度数组,但是这个指针是倒序遍历的.根据短桶原理,一个木桶所能接的最高水位是不高于最短的那个木板的.那么在这里如果水位要跟其中一个柱子的高度持平,那么另一根柱子的高度一定要大于等于前一个柱子的高度.所以我们倒序遍历,找到第一个高于第一个指针所指柱子高度的柱子,那么这跟柱子与前一根柱子所接的水的面积,就是以第一根柱子高度为水位高度的最大面积.因为当高度固定时,宽度越大,面积越大.但是如果第一根柱子在最左边的话,这个最大时成立的,然而,如果柱子不是在最左边,而是中间靠右的位置的话,这个面积就不一定时最大的,因为可能存在这根柱子左边的柱子,使得面积更大.
在这里插入图片描述
在这里插入图片描述
让我们再重新捋一下,首先我们遍历这个数组,对每次遍历到的高度,利用双指针方法判断以p1指针所指高度为水位高度的最大面积,而p2指针既要有倒序遍历的情况,也要考虑正序遍历的情况,比较出两个面积中的最大者,保存为当前面积最大者maxArea.下一次用同样的方法得到的maxArea跟前面一个maxArea进行大小比较,将更大的值赋值给maxArea.所以maxArea保存的时当前最大的水的面积.当遍历结束,最终得到的maxArea就是最大的水的面积,它所对应的两个高度也就随即得出.
利用java编写的参考代码如下:

/*
*   给你n个非负整数a1, a2, ..., an,每个数代表坐标中的一个点(i, ai).
*   在坐标内画n条垂直线,垂直线i的两个端点分别为(i, ai)和(i, 0).找出其中的两条线,使得它们与
*   x轴共同构成的容器可以容纳最多的水.
* */
public class MaxAreaBetweenTwoPillars {

    public static void main(String[] args) {
        int[] height = {1, 8, 6, 2, 5, 4, 8, 3, 7};
        int maxArea = maxArea(height);
        System.out.println(maxArea);
    }
    public static int maxArea(int[] height){
        //最大面积变量,左指针下标,数组height的长度
        int maxArea = 0, left = 0, length = height.length;
        //也是面积变量,因为需要另一个表示面积的变量用来和当前的最大面积比较
        int area;
        //右指针下标
        int right;

        //从前面开始找
        while(left < length){
            right = length - 1;
            while(right > left){
                if(height[right] < height[left]){
                    right--;
                }else{
                    break;
                }
            }
            //计算矩形的面积
            area = height[left] * (right - left);
            //保存计算过的最大的面积
            maxArea = Math.max(maxArea, area);
            left++;
        }
        //上面的遍历完成后,得到的不一定时每个高度所对应的最大面积.
        //从后面开始找
        right = length - 1;
        while(right > 0){
            left = 0;
            while(right > left){
                if(height[right] > height[left]){
                    left++;
                }else{
                    break;
                }
            }
            area = height[right] * (right - left);
            maxArea = Math.max(maxArea, area);
            right--;
        }
        return maxArea;
    }
}

这个方法还可以优化.当我们在遍历这个数组的时候,我们每遍历到一个高度,应该要得到这个高度为水位高度的矩形最大面积.但前面提到这需要遍历两次,一次从后往前遍历,一次从前往后遍历.但我们可以只遍历一次.比如我们考虑下面这个图
在这里插入图片描述
如果我们left指向第一个柱子,那么right从后往前遍历,第一个right所指的即第6个柱子高度小于第一个柱子,虽然对于left而言,并没有找到比left更高的第一个柱子,但是对right而言却找到了第一个比right更高的柱子,因为此时宽度最大,right后面也不可能有其他柱子,所以这个时候的面积就是right高度的最大面积.因为left还没有找到它所对应的最大面积,就要继续令right–;每当right停在一根柱子上,不是left找到比left更高的柱子就是right找到比right更高的柱子,无论怎样我们都可以得到一个最大的面积值,跟之前的maxArea比较即可,然后哪个柱子还没有找到它所对应的最大面积,就让另一个指针向中间靠拢,直到最后left = right.

public static int maxArea1(int[] height){
        int maxArea = 0, left = 0, right = height.length - 1;
        int area = 0;
        while(left < right){
            //计算面积,面积等于宽 * 高, 宽就是left和right之间的距离,
            //left 和 right 所对应的最低高度
            area = Math.min(height[left], height[right]) * (right - left);
            //保存计算过的最大的面积
            maxArea = Math.max(maxArea, area);
            //柱子矮的往中间靠
            if(height[left] < height[right]){
                left++;
            }else{
                right--;
            }
        }
        return maxArea;
    }

其结果是一样的.
以上.
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值