牛客Top200---接雨水问题(java详解)

Posted 小样5411

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客Top200---接雨水问题(java详解)相关的知识,希望对你有一定的参考价值。

题目


分析一(暴力法)

首先暴力破解是最容易想到,且最容易理解的
思路,我们观察可得,第一列和最后一列是不可能接到水的,所以只要看中间即可,我们只要从下标1遍历到arr.length-1,然后获取中间每一列的接水量求和即可,具体怎么求呢?
1、首先获取第i根柱子,左右两边最高的柱子,比如i=1,那么其左边最高的就是arr[0]=3,右边最高就是arr[3]=5,然后取这两根中较短的一根减去柱子i的高度即可,故可得公式Math.min(max_left,max_right)-arr[i],比如i=1,该公式就是arr[0]-arr[1]=3-1=2,故i=1位置上接水量就是2,然后一个个遍历求和即可

核心:当前柱子盛水的多少取决于左右柱子的较小值减去当前元素柱子高度

时间复杂度:O(n2),这个暴力法对于常规数组还行,但是一旦数组长度过长,明显慢了许多,所以需要优化

import java.util.*;

public class Solution {
    /**
     * max water
     * @param arr int整型一维数组 the array
     * @return long长整型
     */
    public long maxWater (int[] arr) {
        // write code here
        //暴力解法
        //思路:对于数组每一个元素,我们需要找到它左边的最高柱子以及它右边的最高柱子
        //而盛水的多少取决于左右柱子的较小值减去当前元素柱子高度
        //即求每一条能接多少,然后求和
        long res=0;//接的雨水量
        int len = arr.length;
        if(len <= 2){
            return res;//至少长度要为3才能接水
        }
        //遍历数组,第一个和最后一个是接不了水的,所以不用遍历
        for(int i = 1 ; i < len - 1 ; i++){
            int max_left = 0,max_right = 0;
            //找i左边最高的柱子
            for(int j = i ; j >= 0 ; j--){
                max_left = Math.max(max_left,arr[j]);
            }
            //找i右边最高的柱子
            for(int j = i ; j <= len - 1 ; j++){
                max_right = Math.max(max_right,arr[j]);
            }
            //每次加上i柱子上接的水,即取i左右最高柱子较小柱子高度-arr[i]就等于i处接水量
            res += (Math.min(max_left,max_right) - arr[i]);
        }
        return res;
    }
}

上面代码没错,但是还可以得到优化,因为复杂度比较高,我们这里直接上时间复杂度为O(n)的双指针法,理解上面解法,可以进一步让我们理解这个双指针法

分析二(双指针法)

双指针法就是用left,right分别指向两端,然后两个指针分别向中间移动,上面只有i遍历,这次left,right都会移动,只遍历一次,直到left > right,则表示遍历完毕,也即所有柱子都遍历了。max_left记录left位置左边最高的柱子,max_right记录right位置右边最高的柱子,核心:当前柱子盛水的多少取决于左右柱子的较小值减去当前元素柱子高度,核心不变
分两种情况:
(1) max_left < max_right (取max_left)
求和,加上max_left-arr[left](接水量),但这里max_left初始为0,max_left-arr[left]<0,所以 Math.max(0, max_left-arr[left])保证>=0,然后更新max_left,因为要保证每次得到left位置左边最大,最后left右移一位
(2) max_left > max_right(取max_right)
同理(1),求和加上接水量,再更新max_right,最后right左移一位

时间复杂度为 O(n)

代码如下:

import java.util.*;

public class Solution {
    public long maxWater (int[] arr) {
        long res = 0;
        int len = arr.length;
        // 特判
        if (len < 3) return res;
        // 双指针法
        int left = 0, right = arr.length - 1;
        long max_left = 0, max_right = 0;//记录左右最大柱子
        while (left <= right) {
            if (max_left < max_right) {
                res += Math.max(0, max_left-arr[left]);//求和
                max_left = Math.max(max_left, arr[left]);//若arr[left]更大则更新为最大
                left++;//left自增,往右移动一次
            } else {
                res += Math.max(0, max_right-arr[right]);//求和
                max_right = Math.max(max_right, arr[right]);//若arr[right]更大则更新为最大
                right--;//right自减,往左移动一次
            }
        }
        return res;
    }
}

以上是关于牛客Top200---接雨水问题(java详解)的主要内容,如果未能解决你的问题,请参考以下文章

牛客Top200---大数加法(java详解)

牛客Top200---最长无重复子数组(java详解)

牛客Top200---链表相加(java详解)

牛客Top200---删除链表中倒数第n个结点(java详解)

牛客Top200---链表中的节点每k个一组翻转(java通俗易懂详解)

牛客Top200---最长递增子序列(求子序列+长度 画图详解java)