Leetcode#84 Largest Rectangle in Histogram


原題地址

 

有兩種方法,左右掃描或輔助棧。

方法I: 左右掃描法
考慮到最大面積的矩形高度一定跟某個條一樣高,所以挨個枚舉每個條,看其向左、向右最多能延伸到多遠。在計算左右邊界時,可以借助之前計算過的結果迭代(類似動歸的感覺)優化以減少時間復雜度,這應該算是唯一的難點了。總的來說,向左一遍,向右一遍,整體求面積再一遍,一共需要3次遍歷,時間復雜度是O(n)。

左右掃描法非常直觀。

代碼:

 1     int largestRectangleArea(vector<int> &height) {
 2         if (height.empty()) return 0;
 3 
 4         int n = height.size();
 5         int maxArea = 0;
 6         int *left = new int[n]; // 向左能延伸多遠
 7         int *right = new int[n]; // 向右能延伸多遠
 8 
 9         // 向右延伸
10         right[n - 1] = 1;
11         for (int i = n - 2; i >= 0; i--) {
12             if (height[i] > height[i + 1])
13                 right[i] = 1;
14             else {
15                 int j = i + 1;
16                 while (j < n && height[j] >= height[i])
17                     j += right[j];
18                 right[i] = j - i;
19             }
20         }
21 
22         // 向左延伸
23         left[0] = 1;
24         for (int i = 1; i < n; i++) {
25             if (height[i] < height[i - 1])
26                 left[i] = 1;
27             else {
28                 int j = i - 1;
29                 while (j >= 0 && height[j] >= height[i])
30                     j -= left[j];
31                 left[i] = i - j;
32             }
33         }
34 
35         // 求面積
36         maxArea = height[0];
37         for (int i = 0; i < n; i++) {
38             maxArea = max(height[i] * (left[i] + right[i] - 1), maxArea);
39         }
40 
41         return maxArea;
42     }

 

 

方法II: 輔助棧法(網上很多人采用的方法)

根本思想是:依次遍歷所有矩形條,嘗試計算以該矩形條為高度的矩形面積。但是在遍歷的時候我們不知道后面還有什么樣的矩形條怎么辦?沒關系,對於沒法確定面積的矩形,壓棧,留着以后處理,而對於那些已經可以確定計算出面積的矩形條,留着也沒用,彈棧。

如果我們能知道一個矩形條向左向右最遠能延伸多遠,我們就能計算出以該矩形條為高的矩形面積了!我們怎么知道向左向右能延伸多遠?觀察下面幾種情況:

情況1,第i個矩形比右邊相鄰的第i+1個矩形高,如下圖所示。意味着,以height[i]為高的矩形的右邊界就是第i個矩形,因為右邊界不能更右了(廢話),也不會在左邊(向左只會讓矩形面積減小)。所以在這種情況下,我們可以立即確定以第i個矩形的高度height[i]為高度的最大矩形面積的右邊界。

情況2,第i個矩形比左邊相鄰的第i-1個矩形高,如下圖所示。意味着,以height[i]為高的矩形的左邊界就是第i個矩形,因為左邊界不能更左了(廢話),也不會出現在右邊(因為向右只會另矩形面積減小)。所以在這種情況下,我們立即就可以確定以第i個矩形的高度height[i]為高度的最大舉行面積的左邊界。

現在,我們依次遍歷各個矩形條,遍歷過的矩形條壓入棧中保存,則不難發現下面的現象:

如果當前矩形條的高度高於棧頂的矩形條的高度,對應上面的情況2,我們可以立即得出,當前矩形一定是以它為高的矩形的左邊界。那么現在我們還不能確定其右邊界,所以除了入棧什么都不用做,如下圖所示:

如果當前矩形條的高度小於或等於棧頂矩形條的高度,對應上面的情況1,我們可以立即得出:棧頂的矩形一定是以它為高的矩形的右邊界所以,我們可以立刻得到以棧頂的矩形條高度為高度的最大矩形的面積(下圖中帶顏色的兩個矩形)!既然我們算出了前一個條最大矩形的面積,那么也就沒必要再留着它了。所以,可以放心把它刪掉,或者說合並。

 

按照上述操作遍歷完所有矩形條之后,棧中的矩形一定是下面這個樣子。(棧里面所有的條肯定是按照高度依次遞增的)

此時,對於任何一個矩形條,我們可以確定,它的右邊界一定是整個棧里所保留的條形圖的最右邊界,而它的左邊界一定是它這個條自己的左邊界,所以,剩余的矩形條也可以立即算出其對應的最大矩形的面積。

其實,只需要在所有的矩形條最后添加一個高度為0的虛擬矩形條,可以省略上面的兩小步。這個虛擬矩形條起收割作用。(見代碼第2行)

顯然時間復雜度是O(n)。

 

具體代碼實現有兩個技巧:

1. 我們只需要在輔助棧保存矩形的右邊界坐標即可,不需要保存高度,因為可以通過右邊界坐標得到(height[i] ),也不需要保存左邊界坐標,因為上一個矩形的右邊界坐標+1就是當前矩形的左邊界。

2. 在直方圖最后添加一個高度為0的虛擬矩形條,這樣保證一次遍歷之后棧里面的矩形都被正確處理過了,否則需要再重復一遍。

代碼:

 1     int largestRectangleArea(vector<int> &height) {
 2         height.push_back(0); // 添加虛擬矩形條
 3         stack<int> st;
 4         int n = height.size();
 5         int maxArea = 0;
 6         int h, w;
 7 
 8         for (int i = 0; i < n; i++) {
 9             if (st.empty() || height[st.top()] < height[i])
10                 st.push(i);
11             else {
12                 while (!st.empty() && height[i] <= height[st.top()]) {
13                     h = height[st.top()];
14                     st.pop();
15                     w = st.empty() ? i : i - (st.top() + 1);
16                     maxArea = max(maxArea, h * w);
17                 }
18                 st.push(i);
19             }
20         }
21 
22         return maxArea;
23     }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM