資料結構與演算法---盛最多水的容器、接雨水

iOS_Asia發表於2020-11-25

盛最多水的容器

11. 盛最多水的容器
給你 n 個非負整數 a1,a2,…,an,每個數代表座標中的一個點 (i, ai) 。在座標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0) 。找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。

說明:你不能傾斜容器。

示例 1:
在這裡插入圖片描述

輸入:[1,8,6,2,5,4,8,3,7]
輸出:49
解釋:圖中垂直線代表輸入陣列 [1,8,6,2,5,4,8,3,7]。在此情況下,容器能夠容納水(表示為藍色部分)的最大值為 49。

示例 2:
輸入:height = [1,1]
輸出:1

示例 3:
輸入:height = [4,3,2,1,4]
輸出:16

示例 4:
輸入:height = [1,2,1]
輸出:2

提示:

n = height.length
2 <= n <= 3 * 104
0 <= height[i] <= 3 * 104

思路1:暴力法

兩根for迴圈
求出每兩根柱子的盛水量,找出最大值

class Solution {
    public int maxArea(int[] height) {
        if(height == null || height.length < 2) return 0;
        int max = 0;
        for(int i = 0; i < height.length - 1; i++){
            for(int j = i + 1; j < height.length; j++){
                max = Math.max(max, (j - i) * Math.min(height[i], height[j]));
            }
        }
        return max;
    }
}

思路2:雙指標

左邊一個指標,右邊一個指標,算出一個值後,讓指標指向值小的柱子往中間走

class Solution {
    public int maxArea(int[] height) {
        if(height == null || height.length < 2) return 0;
        int max = 0;
        int left = 0;
        int right = height.length - 1;

        while(left < right){
           max = Math.max(max, (right - left) * Math.min(height[left], height[right]));
           if(height[left] > height[right]){
               right--;
           }else{
               left++;
           }
        }
        return max;
    }
}

假如現在要將left++,height[left + 1] < height[left],也就是後面的柱子還沒之前的柱子長,則繼續left++
同理,右邊的柱子一樣

class Solution {
    public int maxArea(int[] height) {
        if(height == null || height.length < 2) return 0;
        int max = 0;
        int left = 0;
        int right = height.length - 1;

        while(left < right){
           max = Math.max(max, (right - left) * Math.min(height[left], height[right]));
           if(height[left] > height[right]){
               while((left < right) && (height[right - 1] < height[right])){
                   right--;
               }
               right--;
           }else{
               while((left < right) && (height[left] > height[left + 1])){
                   left++;
               }
               left++;
           }
        }
        return max;
    }
}

MJ的寫法:

class Solution {
    public int maxArea(int[] height) {
        if(height == null || height.length < 2) return 0;
        int max = 0;
        int left = 0;
        int right = height.length - 1;

        while(left < right){
           max = Math.max(max, (right - left) * Math.min(height[left], height[right]));
           if(height[left] > height[right]){
               int minH = height[right];
               while((left < right) && (height[right] <= minH)){
                   right--;
               }
           }else{
               int minH = height[left];
               while((left < right) && (height[left] <= minH)){
                   left++;
               }
           }
        }
        return max;
    }
}

思路是一樣的


接雨水

42. 接雨水
給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖,計算按此排列的柱子,下雨之後能接多少雨水。

在這裡插入圖片描述
輸入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
輸出:6
解釋:上面是由陣列 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種情況下,可以接 6 個單位的雨水(藍色部分表示雨水)。

示例 2:
輸入:height = [4,2,0,3,2,5]
輸出:9

提示:

n == height.length
0 <= n <= 3 * 104
0 <= height[i] <= 105

思路

首先,第一根柱子和最後一根柱子是不能存水的,排除

然後,其餘的柱子的存水量,比如A的存水量,取決於柱子A左邊的所以柱子的最大值leftMax,和柱子A右邊的所以柱子的最大值rightMax,兩個值中比較小的那個,再減去自身的高度,就是存水量

class Solution {
    public int trap(int[] height) {
        //邊界值
        if(height == null || height.length == 0) return 0;

        int lastId = height.length - 2;
        int[] leftMaxes = new int[height.length];
        int[] rightMaxes = new int[height.length];

        leftMaxes[0] = 0;
        rightMaxes[height.length - 1] = 0;
        int leftMax = 0;
        for(int i = 1; i < leftMaxes.length; i++){
            leftMax = Math.max(leftMax, height[i - 1]);
            leftMaxes[i] = leftMax;
        }

        int rightMax = 0;
        for(int i = height.length - 2; i >= 0; i--){
            rightMax = Math.max(rightMax, height[i + 1]);
            rightMaxes[i] = rightMax;
        }


        int sum = 0;
        for(int i = 1; i <= lastId; i++){
            int min = Math.min(leftMaxes[i], rightMaxes[i]);
            if(min > height[i]){
                sum += (min - height[i]);
            }
        }
        return sum;
    }
}

希

從8月份開始,到11月25號,資料結構三季共130+個小時終於看完了
學到了很多,感謝MJ老師
算是演算法入了門,以後的路還很長,希望可以繼續學習,繼續努力
可以刷一遍《劍指offer》
再來一下LeetCode前200道熱門題
加油!

相關文章