1. 程式人生 > 實用技巧 >力扣-84-柱狀圖中最大的矩形

力扣-84-柱狀圖中最大的矩形

方法一、暴力法

從左到右,列舉(固定)高,從左到右迴圈列舉每一根柱子的高度,然後以當前柱子的高度為整個矩形的高度。隨後從這根柱子開始,往左右兩側延伸,直到遇到高度小於矩形高度的柱子,這樣就確定了矩形的左右邊界。程式碼如下:

class Solution {
    public int largestRectangleArea(int[] heights) {
        int len = heights.length;

        if(len == 0){
            return 0;
        }

        if(len == 1){
            
return heights[0]; } int ans = -1; for(int index = 0; index < len; index++){ int preheight = heights[index]; int l = index, r = index; while((l-1>=0) && heights[l-1] >= preheight){  //while找到當前柱子的左邊界 l--; }
while((r+1<len) && heights[r+1] >= preheight){  //while找到當前柱子的右邊界 r++; } ans = Math.max(ans, (r - l + 1) * preheight);  //更新整個矩形面積的最大值 } return ans; } }

時間複雜度:O($n^{2}$)

方法二、單調棧

我們回顧方法一,看看有哪些可以優化的地方,我們發現求解當前柱子的左右邊界的方法非常暴力,我們考慮用什麼樣資料結構進行優化。那麼如何求一根柱子的左側且最近的小於其高度的柱子。我們先看一個結論:

對於兩根柱子$j_{0}$以及$j_{1}$,如果$j_{0}<j_{1}$,並且heights[$j_{0}$] ≥heights[$j_{1}$],那麼對於任意在他們之後出現的柱子$i$,$j_{0}$一定不會是$i$左側最近的且小於其高度的柱子,因$j_{1}$會擋住$j_{0}$,確定$i$的左邊界。那麼我們可以構造一個單調的棧,從小到大存放柱子的index,且還要保證他們的高度也滿足遞增的關係。這樣我們在列舉到第$i$根柱子的時候,就可以先把所有高度大於等heights[i]的值全部移除,棧中剩下的全部小於$i$的高度,那麼棧頂的值即為$i$左邊最近且高度小於$i$的左邊界。同樣的方法我們可以確定每個高度的右邊界。

這裡會有一種特殊情況。如果我們移除了棧中所有的值,那就說明$i$左側所有柱子的高度都大於$i$,那麼我們可以認為$i$左側且最近的小於其高度的柱子在位置$index = -1$,我們可以理解它是一根「虛擬」的、高度無限低的柱子。這樣的定義不會對我們的答案產生任何的影響,我們也稱這根「虛擬」的柱子為「哨兵」。

程式碼如下:

class Solution {
    public int largestRectangleArea(int[] heights) {
        int len = heights.length;
        
        if(len == 0) {
            return 0;
        }
        
        if(len == 1) {
            return heights[0];
        }
        
        Deque<Integer> stack = new LinkedList<Integer>();
        int[] left = new int[len];//儲存每個高度對應的左邊界
        int[] right = new int[len];    //儲存每個高度對應的右邊界
        
     //確定每個柱子的左邊界:從左往右遍歷heights陣列 for(int i=0; i<len; i++) { while(!stack.isEmpty() && heights[i] <= heights[stack.peek()]) { stack.pop(); } left[i] = stack.isEmpty() ? -1:stack.peek();  //如果stack為空,說明柱子的左邊界為-1 stack.push(i); } stack.clear();
    
     //確定每個柱子的右邊界:從右往左遍歷heights陣列
for(int i = len-1; i>=0; i--) { while(!stack.isEmpty() && heights[i] <= heights[stack.peek()]) { stack.pop(); } right[i] = stack.isEmpty()? len:stack.peek();  //如果stack為空,說明柱子的右邊界為n stack.push(i); } int ans = 0; for(int i=0; i<len; i++) { ans = Math.max(ans, (right[i]-left[i]-1)*heights[i]);  //更新矩形的最大高度 } return ans; } }

方法三、單調棧優化