剑指 Offer II 040. 矩阵中最大的矩形(85. 最大矩形)
题目:
思路:
【0】思路源于模拟圆柱的思想
以一下数据为例:
[
{1,0,0,0,1,1,0,0,1},
{1,0,0,0,0,1,0,0,1},
{1,0,0,0,1,1,0,0,1},
{1,0,1,1,1,1,0,0,1},
{1,1,0,1,1,1,0,0,1}
]
首先计算出矩阵的每个元素的左边连续 1 的数量
如最后一行,{1,1,0,1,1,1,0,0,1}
得
[1,2,0,1,2,3,0,0,1]
那么当如果处于下标4的时候值为2
如果往上,上面的位置是3,所以其实是可以取得2*2的矩阵,但是去不了3*3,因为矩阵取决于最小的限制。
再往上便是1,所以矩阵为1*3
其实这里如果从后面往前面遍历会更好,如果取得了最大值,且当前的底边大小*往上的边界值都不如最大值,其实该位置就可以直接跳过了,减少遍历的情况(类似于剪枝)
【1】使用柱状图的优化暴力方法
【2】单调栈的方法
代码展示:
优化的方法:
//时间2 ms击败99.34% //内存43.6 MB击败99.85% class Solution { public int maximalRectangle(char[][] matrix) { int[] heights = new int[matrix[0].length]; int max = 0; for (int i = 0; i < matrix.length; i++) { updateHeights(heights, matrix[i]); max = Math.max(max, largestRectangleArea(heights)); } return max; } private int largestRectangleArea(int[] heights) { int len = heights.length; int max = 0; int[] stack = new int[len]; int k = -1; for (int r = 0; r < len; r++) { while (k != -1 && heights[stack[k]] > heights[r]) { int low = stack[k--]; int l = k == -1 ? -1 : stack[k]; max = Math.max(max, (r - 1 - l) * heights[low]); } stack[++k] = r; } while (k != -1) { int low = stack[k--]; int l = k == -1 ? -1 : stack[k]; max = Math.max(max, (len - 1 - l) * heights[low]); } return max; } public void updateHeights(int[] heights, char[] cells) { for (int j = 0; j < heights.length; j++) { heights[j] = cells[j] == '0' ? 0 : heights[j] + 1; } } }
使用柱状图的优化暴力方法:
//时间13 ms击败52.85% //内存39.7 MB击败96.70% class Solution { public int maximalRectangle(String[] matrix) { int m = matrix.length; if (m == 0) { return 0; } int n = matrix[0].length(); int[][] left = new int[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (matrix[i].charAt(j) == '1') { left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1; } } } int ret = 0; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (matrix[i].charAt(j) == '0') { continue; } int width = left[i][j]; int area = width; for (int k = i - 1; k >= 0; k--) { width = Math.min(width, left[k][j]); area = Math.max(area, (i - k + 1) * width); } ret = Math.max(ret, area); } } return ret; } } //时间17 ms击败45.72% //内存43.4 MB击败99.85% class Solution { public int maximalRectangle(char[][] matrix) { int m = matrix.length; if (m == 0) { return 0; } int n = matrix[0].length; int[][] left = new int[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (matrix[i][j] == '1') { left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1; } } } int ret = 0; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (matrix[i][j] == '0') { continue; } int width = left[i][j]; int area = width; for (int k = i - 1; k >= 0; k--) { width = Math.min(width, left[k][j]); area = Math.max(area, (i - k + 1) * width); } ret = Math.max(ret, area); } } return ret; } }
单调栈的方法:
//时间13 ms击败52.85% //内存42.4 MB击败5.1% class Solution { public int maximalRectangle(String[] matrix) { int m = matrix.length; if (m == 0) { return 0; } int n = matrix[0].length(); int[][] left = new int[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (matrix[i].charAt(j) == '1') { left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1; } } } int ret = 0; for (int j = 0; j < n; j++) { // 对于每一列,使用基于柱状图的方法 int[] up = new int[m]; int[] down = new int[m]; Deque<Integer> stack = new ArrayDeque<Integer>(); for (int i = 0; i < m; i++) { while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) { stack.pop(); } up[i] = stack.isEmpty() ? -1 : stack.peek(); stack.push(i); } stack.clear(); for (int i = m - 1; i >= 0; i--) { while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) { stack.pop(); } down[i] = stack.isEmpty() ? m : stack.peek(); stack.push(i); } for (int i = 0; i < m; i++) { int height = down[i] - up[i] - 1; int area = height * left[i][j]; ret = Math.max(ret, area); } } return ret; } } //时间20 ms击败33.54% //内存43.9 MB击败99.71% class Solution { public int maximalRectangle(char[][] matrix) { int m = matrix.length; if (m == 0) { return 0; } int n = matrix[0].length; int[][] left = new int[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (matrix[i][j] == '1') { left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1; } } } int ret = 0; for (int j = 0; j < n; j++) { // 对于每一列,使用基于柱状图的方法 int[] up = new int[m]; int[] down = new int[m]; Deque<Integer> stack = new LinkedList<Integer>(); for (int i = 0; i < m; i++) { while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) { stack.pop(); } up[i] = stack.isEmpty() ? -1 : stack.peek(); stack.push(i); } stack.clear(); for (int i = m - 1; i >= 0; i--) { while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) { stack.pop(); } down[i] = stack.isEmpty() ? m : stack.peek(); stack.push(i); } for (int i = 0; i < m; i++) { int height = down[i] - up[i] - 1; int area = height * left[i][j]; ret = Math.max(ret, area); } } return ret; } }