1. 程式人生 > 實用技巧 >這就是那個著名的接雨水

這就是那個著名的接雨水

接雨水

今天給大家帶來的是一道特別特別特別經典的題目接雨水問題,這個問題是很多演算法書上面舉例過的題目。雖然是難度題,但是相對來說還是比較容易理解的,程式碼長度也適中,說了這麼多,就一個意思,大家記得打卡這個題目啊,真的是很nice的一道題,下面我們來看一下題目描述。

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

示例 1:

輸入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
輸出:6

示例 2:

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

示例3:

輸入:[4,3,2,0,1,1,5]
輸出:13

上面是由陣列 [4,3,2,0,1,1,5]表示的高度圖,在這種情況下,可以接 13個單位的雨水(見下圖)。

題目解析:

看了上面的示例剛開始刷題的同學可能有些懵逼,那我們結合圖片來理解一下,我們就用示例3的例子進行舉例,他的雨水到底代表的是什麼。

上圖則為我們的題目描述,是不是理解了呢?你也可以這樣理解我們在地上放置了若干高度的黃色箱子,他們中間有空隙,然後我們想在他們裡面插入若干藍色箱子,並保證插入之後,這些箱子的左檢視和右檢視都不能看到藍色箱子。

好啦題目我們已經理解了,下面我們看一下解題思路。做這個這前我們可以先去看一下我們之前做過的另一道題目每日溫度。這兩道題目的思路差不多,都是利用了單調棧的思想,下面我們來看一下具體思路吧。

這裡我們也系統的說一下單調棧,單調棧含義就是棧內的元素是單調的,我們這兩個題目用到的都是遞減棧(相同也可以),我們依次將元素壓入棧,如果當前元素小於等於棧頂元素則入棧,如果大於棧頂元素則先將棧頂不斷出棧,直到當前元素小於或等於棧頂元素為止,然後再將當前元素入棧。就比如下圖的4,想入棧的話則需要2,3出棧之後才能入棧,因為4大於他倆。

我們瞭解單調棧的含義下面我們來看一下接雨水問題到底該怎麼做,其實原理也很簡單,我們通過我們的例3來進行說明。

首先我們依次入棧4,3,2,0我們的陣列前四個元素是符合單調棧規則的。但是我們的第五個1,是大於0的。那我們就需要0出棧1入棧。但是我們這樣做是為了什麼呢?有什麼意義呢?別急我們來看下圖。

上圖我們的,4,3,2,0已經入棧了,我們的另一個元素為1,棧頂元素為0,棧頂下的元素為2。那麼我們在這一層接到的雨水數量怎麼算呢?2,0,1這三個元素可以接住的水為一個單位(見下圖)這是我們第一層接到水的數量。

注:能接到水的情況,肯定是中間低兩邊高,這樣才可以。

因為我們需要維護一個單調棧,所以我們則需要將0出棧1入棧,那麼此時棧內元素為4,3,2,1。下一位元素為1,我們入棧,此時棧內元素為4,3,2,1,1。下一元素為5,棧頂元素為1,棧頂的下一元素仍為1,則需要再下一個元素,為2,那我們求當前層接到的水的數量。

我們是通過2,1,1,5這四個元素求得第二層的接水數為1*3=3;1是因為min(2-1,5-1)=min(1,4)得來的,大家可以思考一下木桶效應。裝水的多少,肯定是按最短的那個木板來的,所以高度為1,3的話是因為5的索引為6,2的索引為2,他們之間共有三個元素(3,4,5)也就是3個單位。所以為6-2-1=3。

將1出棧之後,我們棧頂元素就變成了2,下一元素變成了3,那麼3,2,5這三個元素同樣也可以接到水。

這是第三層的接水情況,能夠接到4個單位的水,下面我們繼續出棧2,那麼我們的4,3,5仍然可以接到水啊。

這是我們第四層接水的情況,一共能夠接到5個單位的水,那麼我們總的接水數加起來,那就是

1+3+4+5=13。你學會了嗎?別急還有動圖我們,我們再來深入理解一哈。

題目程式碼:

class Solution {
    public int trap(int[] height) {
         Stack<Integer> stack = new Stack<Integer>();
         
         int water = 0;
         
         //特殊情況
         
         if(height.length <3){
             return 0;
         }       
         for(int i = 0; i < height.length; i++){
             while(!stack.isEmpty() && height[i] > height[stack.peek()]){
             
                 //棧頂元素
                 
                 int popnum = stack.pop();
                 
                 //相同元素的情況例1,1
                 
                 while(!stack.isEmpty()&&height[popnum] == height[stack.peek()]){
                     stack.pop();
                 }
                 
                 //計算該層的水的單位
                 
                 if(!stack.isEmpty()){
                     int temp = height[stack.peek()];//棧頂元素值
                     
                     //高
                     
                     int hig = Math.min(temp-height[popnum],height[i]-height[popnum]);
                     
                     //寬
                     
                     int wid = i-stack.peek()-1;
                     water +=hig * wid;
                 }

             }
             
             //這裡入棧的是索引
             
             stack.push(i);
         }
         return water;
    }
}

如果能感覺到這個文章寫的很用心的話,能給您帶來一丟丟幫助的話,能麻煩您給這個文章點個贊嗎?這樣我就巨有動力寫下去啦。

另外大家如果需要其他精選演算法題的動圖解析,大家可以微信關注下袁廚的演算法小屋,我是袁廚一個酷愛做飯所以自己考取了廚師證的菜雞程式設計師,會一直用心寫下去的,感謝支援!