1. 程式人生 > >leetcode 84 Largest Rectangle in Histogram (單調棧)

leetcode 84 Largest Rectangle in Histogram (單調棧)

上題目


題目大意為:給一個數組代表一個柱狀圖,陣列中每一個元素依次代表柱狀圖中每個“柱”的高,求這個柱狀圖能包含的最大矩形面積。

思考:最終矩形的高一定等於某個“柱”的高,寬為這個“柱”左右能延伸到的最大距離之和(直到遇見某個“柱”的高低於當前高)。暴力就是對於每一個“柱”,向左和向右延伸到極限得出寬,再乘以高,複雜度是n的平方。

可以用單調棧(棧中的元素單調遞增)對這個過程進行簡化,棧中每個元素包含兩個屬性,一是“柱”高(設為x),二是以這個柱高向左能延伸到的最遠距離(設為y,y包括柱本身的寬度1,故y初值為1),由棧底到棧頂每個元素的柱高x依次遞增,棧頂元素柱高x最大。

整個演算法由不斷的入棧和出棧構成,將給定的陣列元素依次入棧,入棧時計算當前元素向左能延伸到的最遠距離,定義後面要用到的累加寬度w = 0,入棧時有兩種可能:

(1)棧頂元素柱高大於當前元素柱高,出棧(保證棧的單調性,且此時可以計算面積,後面再說)。累加出棧元素的寬度(這個累加寬度代表的是:新的棧頂元素可以向右延伸這個寬度,這也是用遞增棧的原因,由於出棧元素高於新的棧頂元素,那麼出棧元素向左能延伸到的最遠處,新的棧頂元素至少也能向右延伸到),若棧不空且出棧後新的棧頂元素柱高仍高於當前元素,繼續(1)

  (2)  棧頂元素柱高小於當前元素柱高,此時當前元素直接入棧,當前元素的y = 1+w,w代表當前元素之前所有高於當前元素的元素寬度之和,即當前元素可以向左延伸至1+w

出棧時,出棧元素向右能延伸的距離為當前w,故面積為(w+y)*x,計算並更新最大面積。

例如對於題中[2,1,5,6,3,2],以(x,y)表示棧中元素,max代表最大面積

(2,1)準備進棧,初始化w = 0,發現棧空,進棧

(1,1)準備進棧,初始化w = 0,發現其柱高小於棧頂元素,(2,1)出棧,計算面積得 2*1 = 2,max = 2

累加寬度w = w + 1 = 1,y = 1+w = 2,(1,2)進棧

(5,1)準備進棧,初始化w = 0,發現棧頂元素柱高小於當前元素,進棧

(6,1)進棧,同上,此時棧中元素為(1,2)(5,1)(6,1)

(2,1)準備進棧,初始化w = 0,發現其柱高小於棧頂元素,(6,1)出棧,計算面積得 6*1 = 2,max = 6

累加寬度w = w + 1 = 1,接著(5,1)出棧,計算面積得(1 + 1)*5 = 10,max = 10,累加寬度w = w + 1 = 2,出棧完畢。y = 1 + w = 3,(2,3)進棧,此時棧中元素為(1,2)(2,3)

(3,1)進棧,此時棧中元素為(1,2)(2,3)(3,1)

將棧中剩餘元素依次出棧:

初始化w = 0,(3,1)出棧,計算面積得 3*1 = 8,max不變,累加寬度w = w + 1 = 1,接著(2,3)出棧,計算面積得(1+3)*2 = 8,max不變,累加寬度w = w + 3 = 4,接著(1,2)出棧,計算面積得(4+2)*1 = 6,面積不變,出棧完畢。

最終max = 10

上AC程式碼:

class Solution {
    private static class Node{
        public int height;
        public int weight;

        public Node(int height, int weight) {
            this.height = height;
            this.weight = weight;
        }
    }
    public int largestRectangleArea(int[] heights) {
        if(heights==null||heights.length==0)
            return 0;
        int max = 0;
        Stack<Node> stack = new Stack<>();
        int index = 0;
        stack.push(new Node(heights[index++],1));
        Node temp;
        int areaSize ,count;
        for(;index<heights.length;index++) {
            count = 0;
            while (!stack.empty() && stack.peek().height >= heights[index]) {
                temp = stack.pop();
                count += temp.weight;
                areaSize = temp.height*count;
                max = max > areaSize ? max : areaSize;
            }
            stack.push(new Node(heights[index],count+1));
        }
        count = 0;
        while (!stack.empty()){
            temp = stack.pop();
            count += temp.weight;
            areaSize = temp.height*count;
            max = max > areaSize ? max : areaSize;
        }
        return max;
    }
}
PS:開始在POJ 2559 (題目一樣)上提交,怎麼改都是wrong answer,後來只好去leetcode,發現是函式的最開始沒有判斷陣列是否為空 orz,以後一定記得判斷!!!