LeetCode 筆記系列 18 Maximal Rectangle [學以致用]


題目: Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.

leetcode的題目真是越來越經典了。比如這個題目,就是一道男默女淚的題。

一般人拿到這個題目,除非做過類似的,很難一眼找出一個方法來,更別說找一個比較優化的方法了。

首先一個難點就是,你怎么判斷某個區域就是一個矩形呢?

其次,以何種方式來遍歷這個2D的matrix呢?

一般來說,對這種“棋盤式”的題目,像什么Queen啦,象棋啦,數獨啦,如果沒有比較明顯的遍歷方式,可以采用一行一行地遍歷。(好像廢話哦。。。)

然后,當遍歷到(i, j)的時候,該做什么樣的事情呢?想想,嗯,那我可不可以簡單看看,以(i,j)為矩形左上角,能不能形成一個矩形,能不能形成多個矩形?那形成的矩形中,我們能不能找一個最大的呢?(有同學問,為毛你要以這個點為左上角,不為左下角,或者其他腳哩?因為我們打算從左到右,從上到下一行一行遍歷嘛,這樣就不會漏掉,說不定還能做一些優化)

首先,如果(i, j)是0,那肯定沒法是矩形了。

如果是1,那么我們怎么找以它為左上角的矩形呢?呼喚畫面感!

。。。你TM在逗我?==b

圖中圈圈表示左上角的1,那么矩形的可能性是。。。太多啦,怎么數嘛!

我們可以試探地從左上角的1所在的列開始,往下數數,然后呢,比如在第一行,例如是藍色的那個矩形,我們看看在列上,它延伸了多遠,這個面積是可以算出來的。

然后繼續,第二行,例如是那個紅色的矩形,再看它延伸到多遠,哦,我們知道,比第一行近一些,我們也可以用當前離第一行的行數,乘以延伸的距離,得到當前行表示的矩形面積。

但是到了第一個虛線的地方,它遠遠超過了上面的其他所有行延伸的距離了,注意它的上方都是空心的哦,所以,我們遇到這種情況,計算當前行和左上角1圍成的面積的時候,只能取所有前面最小的延伸距離乘以當前離第一行的行數。其實,這對所有情況都是這樣的,是吧?於是,我們不是就有方法遍歷這些所有的矩形了嘛。

代碼如下:

 1     /**
 2      * 以給出的坐標作為左上角,計算其中的最大矩形面積
 3      * @param matrix
 4      * @param row 給出坐標的行
 5      * @param col 給出坐標的列
 6      * @return 返回最大矩形的面積
 7      */
 8     private int maxRectangle(char[][] matrix, int row, int col) {
 9         int minWidth = Integer.MAX_VALUE;
10         int maxArea = 0;
11         for (int i = row; i < matrix.length && matrix[i][col] == '1'; i++) {
12             int width = 0;
13             while (col + width < matrix[row].length
14                     && matrix[i][col + width] == '1') {
15                 width++;
16             }
17             if (width < minWidth) {// 如果當前寬度小於了以前的最小寬度,更新它,為下面的矩形計算做准備
18                 minWidth = width;
19             }
20             int area = minWidth * (i - row + 1);
21             if (area > maxArea)
22                 maxArea = area;
23         }
24         return maxArea;
25     }
maxRectangle

這樣,我們再對每個點都調用一下上面的這個方法,不是就能求出最大面積了么。

解法一:

public int maximalRectangle(char[][] matrix) {
        // Start typing your Java solution below
        // DO NOT write main() function
        int m = matrix.length;
        int n = m == 0 ? 0 : matrix[0].length;
        int maxArea = 0;
        for(int i = 0; i < m; i++){//row
            for(int j = 0; j < n; j++){//col
                if(matrix[i][j] == '1'){
                    int area = maxRectangle(matrix, i, j);
                    if(area > maxArea) maxArea = area;
                }
            }
        }
        return maxArea;
     }
View Code

這個需要O(n3),所以沒有通過大集合的測試。

leetcode的討論組給出了一個比較難理解的方法,這里就不采用了。

說說第三個方法。前一個筆記,我們討論了柱狀圖的最大矩形面積,那可以O(n)的,學以致用呀!btw,leetcode的這兩題也是挨一塊兒的,用心良苦。。。。

如果我們把每一行看成x坐標,那高度就是從那一行開始往上數的1的個數。帶入我們的maxAreaInHist方法,在O(n2)時間內就可以求出每一行形成的“柱狀圖”的最大矩形面積了。它們之中最大的,就是我們要的答案。

代碼如下:

 1 public int maximalRectangle2(char[][] matrix) {
 2         int m = matrix.length;
 3         int n = m == 0 ? 0 : matrix[0].length;
 4         int[][] height = new int[m][n + 1];
 5         //actually we know that height can just be a int[n+1], 
 6         //however, in that case, we have to write the 2 parts together in row traverse,
 7         //which, leetcode just doesn't make you pass big set
 8         //所以啊,leetcode是喜歡分開寫循環的,即使時間復雜度一樣,即使可以節約空間
 9         int maxArea = 0;
10         for(int i = 0; i < m; i++){
11             for(int j = 0; j < n; j++) {
12                 if(matrix[i][j] == '0'){
13                     height[i][j] = 0;
14                 }else {
15                     height[i][j] = i == 0 ? 1 : height[i - 1][j] + 1;
16                 }
17             }
18         }
19         for(int i = 0; i < m; i++){
20             int area = maxAreaInHist(height[i]);
21             if(area > maxArea){
22                 maxArea = area;
23             }
24         }
25         return maxArea;
26      }
27      
28      private int maxAreaInHist(int[] height){
29          Stack<Integer> stack = new Stack<Integer>();
30          int i = 0;
31          int maxArea = 0;
32          while(i < height.length){
33              if(stack.isEmpty() || height[stack.peek()] <= height[i]){
34                  stack.push(i++);
35              }else {
36                  int t = stack.pop();
37                  maxArea = Math.max(maxArea, height[t] * (stack.isEmpty() ? i : i - stack.peek() - 1));
38              }
39          }
40          return maxArea;
41      }
View Code

這里有一個和leetcode相關的細節。就是本來在計算height數組的時候,我們沒有必要分配成代碼中的那個樣子,一維就可以了,然后在遍歷每一行的時候計算當前行的height數組,然后再計算maxArea。這種情況下還是過不了大集合,所以不得不為每一行都保存一個height,先期計算該二維數組。

總結:

1. 學到的新知識要用;

2. 畫面感和邏輯分析都很重要,不可偏非。

 

 

 

 


免責聲明!

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



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