85. 最大矩形
目录:
题目
给定一个仅包含 0
和 1
、大小为 rows x cols
的二维二进制矩阵,找出只包含 1
的最大矩形,并返回其面积。
示例 1:
![](https://img2022.cnblogs.com/blog/1852906/202206/1852906-20220615092759458-1515853829.png)
输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:6
解释:最大矩形如上图所示。
示例 2:
输入:matrix = []
输出:0
示例 3:
输入:matrix = [["0"]]
输出:0
示例 4:
示例 5:输入:matrix = [["1"]]
输出:1
输入:matrix = [["0","0"]]
输出:0
提示:
- rows == matrix.length
- cols == matrix[0].length
- 1 <= row, cols <= 200
- matrix[i][j] 为 '0' 或 '1'
单调栈法
每一层看作是柱状图,可以套用84题柱状图的最大面积的单调栈法。
- 第一层柱状图的高度["1","0","1","0","0"],最大面积为1;
- 第二层柱状图的高度["2","0","2","1","1"],最大面积为3;
- 第三层柱状图的高度["3","1","3","2","2"],最大面积为6;
- 第四层柱状图的高度["4","0","0","3","0"],最大面积为4;
class Solution { public int maximalRectangle(char[][] matrix) { if(matrix.length==0 || matrix[0].length==0) return 0; int ret = 0; int row = matrix.length; int col = matrix[0].length; int[] heights = new int[col]; for(int i = 0;i<row;i++) { for(int j = 0;j<col;j++) { /** *求当前第i行往上连续1的个数,不连续就置为0 * 高度必须从底部起开始算起 * heights是逐行更新,当前行的更新是在上一行结果 * 的基础上进行的 */ if(matrix[i][j]=='1') heights[j]+=1; else//一票否决 heights[j] = 0; } ret = Math.max(ret,largestRectangleArea(heights)); } return ret; } /** * 84. 柱状图中最大的矩形 */ public int largestRectangleArea(int[] heights) { Deque<Integer> stk = new ArrayDeque<>(); int area = 0,n = heights.length; for(int i = 0; i < n; ++i) { while(!stk.isEmpty() && heights[i] < heights[stk.peek()]) { int h = heights[stk.peek()]; stk.pop(); int w = stk.isEmpty() ? i : i-1-stk.peek(); area = Math.max(h * w, area); } stk.push(i); } while(!stk.isEmpty()) { int h = heights[stk.peek()]; stk.pop(); int w = stk.isEmpty() ? n : n-1-stk.peek(); area = Math.max(h * w, area); } return area; } }
动态规划法
- 循环遍历以(i,j)为右下角的最大矩形面积
- 辅助数组dp的含义:
-
- dp[i][j][0]:以(i,j)为起点,向左连续1的个数
-
- dp[i][j][1]:以(i,j)为起点,向上连续1的个数
class Solution { public int maximalRectangle(char[][] matrix) { if (matrix.length == 0 || matrix[0].length==0) return 0; int m = matrix.length; int n = matrix[0].length; int ret=0; /* * 0-> width * 1-> height */ int[][][] dp = new int[m][n][2]; //左上角 if(matrix[0][0]=='1') { dp[0][0][0]=1; dp[0][0][1]=1; ret = 1; } //第一行 for(int j = 1;j < n;j++) { if(matrix[0][j]=='1') { dp[0][j][0] = dp[0][j-1][0]+1; dp[0][j][1] = 1; ret = Math.max(ret,dp[0][j][0]); } } //第一列 for(int i=1;i<m;i++) { if(matrix[i][0]=='1') { dp[i][0][0]=1; dp[i][0][1]=dp[i-1][0][1]+1; ret=Math.max(ret,dp[i][0][1]); } } //其他(i,j) for(int i = 1;i < m;i++) { for(int j = 1;j < n;j++) { if(matrix[i][j]=='1') { dp[i][j][0]=dp[i][j-1][0]+1; dp[i][j][1]=dp[i-1][j][1]+1; int width=dp[i][j][0]; int height=dp[i][j][1]; int minHeight=height; /* * 遍历以(i,j)为右下角、宽度为k所能构成的 * 的矩形面积 */ for(int k = 1;k <= width;k++) { /* *此时所能构成的矩形的高度为以k为宽度的所有立柱 *中从底部起的1的连续值的最小值,由于我们是逐渐增加 *宽度的,所以当前的最小值等于新增加的一列的值和之前 * 的最小值的最小值 */ minHeight=Math.min(minHeight,dp[i][j-k+1][1]); ret=Math.max(ret,k*minHeight); } } } } return ret; } }
动态规划方法的另一种实现:
- dp[i][j]为以(i,j)为起始点向上连续1的个数
- 至于以(i,j)为起始点向左连续1的个数我们可以k <= j+1 && matrix[i][j-k+1]!='0';代替
class Solution{ public int maximalRectangle(char[][] matrix) { int m = matrix.length, n = matrix[0].length; int ret = 0; // dp[i][j]为以(i,j)为起始点向上连续1的个数 int[][] dp = new int[m][n]; for(int i = 0; i < m; i++) { for(int j = 0; j < n; j++) { if(matrix[i][j] == '0') continue; if(i==0) dp[i][j] = 1; else dp[i][j] = dp[i-1][j] + 1; int minHeight = dp[i][j]; for(int k = 1; k <= j+1 && matrix[i][j-k+1]!='0'; k++) { minHeight=Math.min(minHeight,dp[i][j-k+1]); ret=Math.max(ret,k*minHeight); } } } return ret; } }