1. 程式人生 > 其它 >接雨水

接雨水

技術標籤:刷題資料結構演算法leetcodepython動態規劃

接雨水

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

上面是由陣列 [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

方法一:依次計算當前位置能接住的雨水

當前位置能接住的雨水等於min(左邊最高的柱子,右邊最高的柱子)- 當前位置的高度
可以先從後向前遍歷一次陣列,記錄當前位置右邊最高的柱子
左邊最高的柱子可以在計算過程中更新

public int trap(int[] height) {
    if(height.length == 0)  return 0;
    int ans = 0;
    int leftMax = height[0];
    int[] rightMax = new int[height.length];
    for (int i = height.length - 2; i >= 0; i--) {
        rightMax[i] = Math.max(rightMax[i + 1], height[i + 1]);
    }
    for (int i = 1; i < height.length - 1; i++) {
        int h = Math.min(leftMax, rightMax[i]);
        if (h > height[i]) {
            ans += h - height[i];
        }
        leftMax = Math.max(leftMax, height[i]);
    }
    return ans;
}

時間複雜度O(n)
空間複雜度O(n)

方法二:雙指標
在方法一的基礎上,不需要用陣列來存右邊最大高度,只需要用一個變數

  • 如果左邊最大高度小於右邊,說明最小的高度由左邊決定
  • 如果左邊最大高度小於右邊,說明最小的高度由右邊決定
public int trap(int[] height) {
    if (height.length == 0) return 0;
    int ans = 0;
    int leftMax = height[0];
    int rightMax = height[height.length - 1];
    int low = 1;
    int high = height.length - 2;
    while (low <= high) {
        while (low <= high && leftMax <= rightMax) {
            if (leftMax > height[low]) {
                ans += leftMax - height[low];
            }
            leftMax = Math.max(leftMax, height[low]);
            low++;
        }
        while (low <= high && leftMax > rightMax) {
            if (rightMax > height[high]) {
                ans += rightMax - height[high];
            }
            rightMax = Math.max(rightMax, height[high]);
            high--;
        }
    }
    return ans;
}

時間複雜度O(n)
空間複雜度O(1)

方法三:單調遞減棧
用棧來跟蹤可能儲水的最長的條形塊,在遍歷陣列時維護一個棧,如果當前的條形塊小於或等於棧頂的條形塊,我們將條形塊的索引入棧,意思是當前的條形塊被棧中的前一個條形塊界定。如果我們發現一個條形塊高於棧頂,我們可以確定棧頂的條形塊被當前條形塊和棧的前一個條形塊界定,因此我們可以彈出棧頂元素並且累加答案

public int trap(int[] height) {
    int ans = 0;
    Stack<Integer> stack = new Stack<>();//存下標
    for (int i = 0; i < height.length; i++) {
        while (!stack.isEmpty() && height[stack.peek()] < height[i]){
            int low = height[stack.pop()];//低窪
            if (stack.isEmpty()) {
                break;
            }
            int boundHeight = Math.min(height[stack.peek()], height[i]);
            int distance = i - stack.peek() - 1;//寬度
            ans += (boundHeight - low) * distance;
        }
        stack.push(i);
    }
    return ans;
}

時間複雜度O(n)
空間複雜度O(n)