「前端刷题」85. 最大矩形

306 阅读1分钟

「这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战」。

题目

链接:leetcode-cn.com/problems/ma…

给定一个仅包含 01 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

 

示例 1:

**输入:**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:

**输入:**matrix = [["1"]] **输出:**1

示例 5:

**输入:**matrix = [["0","0"]] **输出:**0

 

提示:

  • rows == matrix.length
  • cols == matrix[0].length
  • 0 <= row, cols <= 200
  • matrix[i][j]'0''1'

解题思路

  1. 二维坐标转换成多个柱状图
    • 柱子为二维坐标中连续的 '1'
    • 每个柱状图以 i 号行向右为横坐标,0 号列向上为纵坐标
  2. 针对每个柱状图,求其中的最大矩形面积
    • 针对任一柱子 top ,向左右找首个严格低于自身的柱子 left/right * 从一维无序数组中的某个元素向左右找首个严格小于/大于自身的元素,可以用单调栈实现 * 本题要找严格低于自身的元素,所以设计单调栈严格递增,出栈时,很容易以 O(1) 时间找到出栈柱子的 left/right 柱子
    • 矩形面积就是 heights[top] * width , width = (right - 1) - (left + 1) + 1
    • 每个都作为柱子 top ,就能打擂台求出柱状图的的最大矩形面积
  3. 各个柱状图的最大矩形面积打擂台,得到二维坐标中的最大矩形面积

代码

var maximalRectangle = function(matrix) {
  if (!matrix || !matrix.length || !matrix[0].length) return 0
  const ROWS = matrix.length, COLS = matrix[0].length
  // heightsList[i][j] 表示以 matrix[i] 行向右为横坐标、以 0 号列向上为纵坐标,
  // 构成的柱状图中 j 号柱子的高度,柱子为连续的1
  const MOD = 2 // heightsList[i] 最多向前依赖 heightsList[i - 1],使用滚动数组节省空间
  let heightsList = Array.from({length: MOD}, () => new Array(COLS))
  let maxArea = 0
  for (let i = 0; i < ROWS; ++i) { // O(ROWS)
    for (let j = 0; j < COLS; ++j) { // O(COLS)
      heightsList[i % MOD][j] = matrix[i][j] === '1' ?
        (i - 1 >= 0 ? heightsList[(i - 1) % MOD][j] : 0) + 1 :
        0
    }
    maxArea = Math.max(maxArea, getMaxArea(heightsList[i % MOD]))
  }
  // console.log(heightsList)
  return maxArea

  // 求柱状图中的最大矩形面积
  // 对任一柱子 top ,向左右找到首个严格低于自身的柱子 left/right
  // 矩形面积就是 heights[top] * width
  // width = (right - 1) - (left + 1) + 1
  // 用单调栈实现向左右找首个柱子,单调严格递增
  function getMaxArea(heights) {
    // NOTICE:
    // 前哨兵:防止栈空,防止越界
    // 后哨兵:确保每个入栈的元素最终都经历出栈
    // 严格递增,前哨兵要小于后哨兵,否则后哨兵会让栈空,导致求 left 时越界
    heights = [-2, ...heights, -1]
    const N = heights.length
    let maxArea = 0, stack = []
    for (let i = 0; i < N; ++i) { // O(COLS + 2) = O(COLS) ,因为每个元素都只经历一次入栈、出栈
      while (stack.length) {
        const top = stack[stack.length - 1]
        if (heights[i] > heights[top]) break
        stack.pop()
        const left = stack[stack.length - 1], right = i
        const width = (right - 1) - (left + 1) + 1
        maxArea = Math.max(
          maxArea,
          width * heights[top]
        )
      }
      stack.push(i)
    }
    return maxArea
  }
}