leetcode#42 Trapping rain water的五種解法詳解

leetcode#42 Trapping rain water

這道題十分有意思,能夠用不少方法作出來,每種方法的思想都值得讓人細細體會。算法

42. Trapping Rain Water
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.數組

For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.app

 Solution 1:優化

經過分別計算每一座標i上有多少水,進而將其相加獲得答案。spa

問題是咱們如何知道每一座標i上有多少水呢?仔細思考,其實只有出現「兩高夾一矮」纔可能會存到水,以下圖所示。指針

進而,咱們能夠想到:每一座標i上存多少水是由 1.其自身高度 2.它左邊的最高高度left_most 3.它右邊的最高高度right_most三種因素決定的。code

當 min{ left_most, right_most} 小於或等於其自身高度時,它能存的水就是0,好比array[1]=1,其left_most= array[0]=0, 其right_most=array[7]=3, min{left_most, right_most}=left_most=0< height= array[1]=1,這也就是說座標1 存不了水。blog

當min{ left_most,right_most} 大於其自身高度時,這時這三者間出現了「兩高夾一矮」的狀況,故其能存水,並且其存水數= min{left_most,right_most} - height。遞歸

咱們分別來對一些座標進行驗證:leetcode

座標1,存水數=0.//正確

座標2,leff_most=1,right_most=3,存水數=left_most-height=1-0=1.//正確

座標3,left_most=1,right_most=3,min{left_most,right_most}=1=height,存水數=0.//正確

讀者能夠對每一個座標進行驗證,會發現以上結論皆是正確的。因此,如今咱們的solution就出來了,咱們只須要求出每一個座標對應的left_most和right_most,再把存水數相加,就是總的存水數了。

因此,很樸素天然的一個想法就是,遍歷一遍數組,對每一個數組元素遍歷左邊一次求出left_most,遍歷右邊一次求出right_most。

代碼以下,

 

//29ms 6.36%
//complexity: O(N^2)
int trap(vector<int>& height)
{
int ans = 0;
int size = height.size();
for (int i = 1; i < size - 1; i++) {
int max_left = 0, max_right = 0;
for (int j = i; j >= 0; j--) { //Search the left part for max bar size
max_left = max(max_left, height[j]);
}
for (int j = i; j < size; j++) { //Search the right part for max bar size
max_right = max(max_right, height[j]);
}
ans += min(max_left, max_right) - height[i];
}
return ans;
}

 

Solution 2:

在solution 1裏,咱們已經知道只要求出left_most和right_most,就能夠求出答案,那能不能優化一下求這兩個數的過程呢?固然是能夠的,咱們只須要左遍歷一次數組,右遍歷一次數組,便可獲得left_most和right_most。

/*Solution2: 上一種方法其實有優化的空間
經過兩次for循環可分別求得left_most和right_most,第三次for循環便可求得sum,
complexity: O(n)
*/
int trap(vector<int>& height)
{
    if(height == null)
        return 0;
    int ans = 0;
    int size = height.size();
    vector<int> left_max(size), right_max(size);
    left_max[0] = height[0];
    for (int i = 1; i < size; i++) {
        left_max[i] = max(height[i], left_max[i - 1]);
    }
    right_max[size - 1] = height[size - 1];
    for (int i = size - 2; i >= 0; i--) {
        right_max[i] = max(height[i], right_max[i + 1]);
    }
    for (int i = 1; i < size - 1; i++) {
        ans += min(left_max[i], right_max[i]) - height[i];
    }
    return ans;
}

 

 

Solution 3:

這裏再介紹一種優化方法,雙指針法,在數組首尾分別建立一個指針,兩指針相見時結束循環。

int trap(vector<int>& height)
{
    int left = 0, right = height.size() - 1;
    int ans = 0;
    int left_max = 0, right_max = 0;
    while (left < right) {
        if (height[left] < height[right]) {
            height[left] >= left_max ? (left_max = height[left]) : ans += (left_max - height[left]);
            ++left;
        }
        else {
            height[right] >= right_max ? (right_max = height[right]) : ans += (right_max - height[right]);
            --right;
        }
    }
    return ans;
}

 

Solution 4:

既然能夠縱向的求存水數,那咱們能不能一層一層的求存水數呢?

這是第一層,當咱們遇到一個空的,且不在邊界,存水數+1,因此第一層咱們在i=2,i=5 時分別+1.

第二層,存水數+4,依次類推,最終能夠求出答案。

代碼筆者就不給了,讀者有興趣的能夠本身寫來試試。

 

Soluton 5:

這是在leetcode中solution給出的一種很新穎的解法,利用了棧的結構,經過維護一個非遞增棧來獲得答案。

本質思想仍是利用了要存水必須是「兩高夾一矮」這個特色,只不過這裏是用非遞增棧來實現。

下面定義一些符號以便理解:

stack[-1] 棧頂元素

stack[-2] 棧頂的下面一個元素(即倒數第二個元素)

solution4的整個算法是這麼實現的:遍歷數組,遇到一個元素時,將其與棧頂元素比較,若是其小於等於棧頂元素,直接壓棧,將其放入棧中(爲維護非遞增棧的結構,不能將比棧頂元素大的元素壓棧),

如果其大於棧頂元素,此時必定造成了一個「兩高夾一矮」局面,由於棧是非遞增棧,因此 stack[-1]<stack[-2],又 current>stack[-1],因此是一個「兩高夾一矮」局面,此時算完存水數後棧頂元素出棧,繼續判斷,

遞歸處理便可。

在上例中整個過程是這樣的。

step0: 0不入棧

step1: 1>0 array[1] 入棧 棧:[1]

step2: 0<stack[-1]=1 入棧 棧:[1,0]

step3: 2>stack[-1]=0 存水數+1,0出棧,2>stack[-1]=1, 此時stack內元素不足2,不足以造成「兩高夾一矮」局面, 1出棧,2入棧 棧:[2]

step4: 1<stack[-1]=2 1入棧 棧:[2,1]

step5: 0<stack[-1]=1 0入棧 棧:[2,1,0]

step6: 1>stack[-1]=0 存水數+1,0出棧 1=stack[-1] 1入棧 棧:[2,1,1] 

step7: 3>stack[-1]=1 存水數+0,1出棧 3>stack[-1]=1 存水數+3,1出棧 3>stack[-1]=2 存水數+0 2出棧 3入棧 棧:[3]

step8: 2<stack[-1] 2入棧 棧:[3,2]

step9: 1<stack[-1] 1入棧 棧:[3,2,1]

step10: 2>stack[-1] 存水數+1 1出棧 2入棧 棧:[3,2,2]

step 11:1<stack[-1] 入棧 棧:[3,2,2,1] 

done

/*Solution4
Stack solution
這個solution利用了棧結構,經過維護一個非遞增棧,一步一步算出ans
*/

int trap(vector<int>& height)
{
    int ans = 0, current = 0;
    stack<int> st;
    while (current < height.size()) {
        while (!st.empty() && height[current] > height[st.top()]) {
            int top = st.top();
            st.pop();
            if (st.empty())
                break;
            int distance = current - st.top() - 1;
            int bounded_height = min(height[current], height[st.top()]) - height[top];
            ans += distance * bounded_height;
        }
        st.push(current++);
    }
    return ans;
}
相關文章
相關標籤/搜索