【笔记】JavaScript版数据结构与算法——数据结构之“栈”(85. 最大矩形)。。。

85. 最大矩形

1.题目

85. 最大矩形 - 力扣(LeetCode)

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例:

  • 输入:
[
  ["1","0","1","0","0"],
  ["1","0","1","1","1"],
  ["1","1","1","1","1"],
  ["1","0","0","1","0"]
]
  • 输出: 6

题目模板

/**
 * @param {character[][]} matrix
 * @return {number}
 */
var maximalRectangle = function(matrix) {

};

2.思路分析

细节详见题解


对角线法

3.所用到的方法

见题解

4.题解及优化

课程题解

课程题解绕开了好几种边缘情况。。。,参考价值不大。。。

export default (arr) => {
  let result = []
  let reg = /1{2,}/g
  // 把二位数组重新表达,把相邻的1提取出来(起始点+截止点)
  arr = arr.map(item => {
    let str = item.join('')
    let r = reg.exec(str)
    let rs = []
    while (r) {
      rs.push([r.index, r.index + r[0].length - 1])
      r = reg.exec(str)
    }
    return rs
  })
  // 通过递归计算相邻的矩阵
  let maxRect = (arr, result, n = 1) => {
    // 弹出第一行
    let top = arr.pop()
    // 弹出第二行
    let next = arr.pop()
    // 记录第一行的每一个起始点和截止点
    let tt
    // 记录第二行的每一个起始点和截止点
    let nn
    // 记录交叉的起始索引
    let start
    // 记录交叉的截止索引
    let end
    let width = 1
    let maxWidth = 1
    n++
    for (let i = 0, il = top.length; i < il; i++) {
      tt = top[i]
      for (let j = 0, jl = next.length; j < jl; j++) {
        nn = next[j]
        width = Math.min(tt[1], nn[1]) - Math.max(tt[0], nn[0])
        // 修改避免相邻两个数的差值为1(实际宽度为2)没有为start,end赋值导致的bug,应该加上=
        if (width >= maxWidth) {
          maxWidth = width
          start = Math.max(tt[0], nn[0])
          end = Math.min(tt[1], nn[1])
        }
      }
    }
    // 如果没有找到交叉点
    if (start === undefined || end === undefined) {
      if (n < 3) {
        return false
      } else {
        width = top[0][1] - top[0][0] + 1
        if (width > 1) {
          result.push((n - 1) * width)
        }
      }
    } else {
      // 找到交叉点继续下一行
      if (arr.length > 0) {
        arr.push([
          [start, end]
        ])
        maxRect(arr, result, n++)
      } else {
        // 从某一行一直计算到最后一行,这个时候start和end一直有值,所以不会进入到if层,这个时候n就是累计的行数(高),end-start+1就是宽
        result.push(n * (end - start + 1))
      }
    }
  }
  while (arr.length > 1) {
    maxRect([].concat(arr), result)
    arr.pop()
  }
  // 取最大值
  let max = 0
  let item = result.pop()
  while (item) {
    if (item > max) {
      max = item
    }
    item = result.pop()
  }
  return max > 0 ? max : -1
}

我的题解

根据课程题解改编,过了如下几种边缘情况:

// console.log('结果:', maximalRectangle([['1']])) // 1
// console.log('结果:', maximalRectangle([['0']])) // 0
// console.log('结果:', maximalRectangle([['0', '1'], ['0', '1']])) // 2
// console.log('结果:', maximalRectangle([
//   ['1', '0', '1', '1', '1'],
//   ['0', '1', '0', '1', '0'],
//   ['1', '1', '0', '1', '1'],
//   ['1', '1', '0', '1', '1'],
//   ['0', '1', '1', '1', '1']
// ])) // 6
// console.log('结果:', maximalRectangle([['0', '1'], ['1', '0']])) // 1

最终卡在了下面两种边缘情况

console.log('结果:', maximalRectangle([
  ['0', '0', '0', '1', '0', '1'],
  ['0', '1', '1', '0', '0', '1'],
  ['1', '0', '1', '1', '1', '1']
])) // 4
console.log('结果:', maximalRectangle([['1'], ['0'], ['1'], ['1'], ['1'], ['1'], ['0']])) // 4

详细代码如下:

let maximalRectangle = matrix => {
  let result = []
  let reg = /1{1,}/g
  // 把二位数组重新表达,把相邻的1提取出来(起始点+截止点),重新赋值到matrix
  matrix = matrix.map(item => {
    let str = item.join('') // 每个item转为string
    let r = reg.exec(str)
    let rs = [] // 存放
    while (r) {
      // r.index:第一个1的位置;r.index + r[0].length - 1:最后一个1的位置
      rs.push([r.index, r.index + r[0].length - 1])
      r = reg.exec(str) // 继续下一次匹配
    }
    return rs
  })
  /**
   * 通过递归计算相邻的矩阵
   * @param arr:需要进行操作的数组
   * @param result,之前处理得到的结果
   * @param n,当前需要向下找交叉的行号(1~arr.length)
   * @returns {boolean}
   */
  let maxRect = (arr, result, n = 1) => {
    // 弹出第一行(这里使用pop相当于是倒着来,使用shift才是正着来,把arr看做栈,后面的先处理)
    let top = arr.pop()
    // 弹出第二行
    let next = arr.pop() || []
    // 记录第一、二行的每一个起始点和截止点,交叉的起始、截止索引
    let tt, nn, start, end, temp
    let width = 1
    let maxWidth = 1
    n++ // 目前n的值代表的是处理过的行数
    top.map(item => {
      if (item[1] - item[0] > 0) {
        result.push(item[1] - item[0] + 1)
      }
    })
    for (let i = 0, il = top.length; i < il; i++) {
      tt = top[i]
      for (let j = 0, jl = next.length; j < jl; j++) {
        nn = next[j]
        width = Math.min(tt[1], nn[1]) - Math.max(tt[0], nn[0]) // 最小end-最大start
        // 修改避免相邻两个数的差值为1(实际宽度为2)没有为start,end赋值导致的bug,应该加上=
        // 当矩形宽度变小时,递归回退,矩形面积为(n - 1) * (top[0][1] - top[0][0] + 1)
        // 否则,矩形面积为n * (end - start + 1)
        if (width >= maxWidth) {
          maxWidth = width
          start = Math.max(tt[0], nn[0])
          end = Math.min(tt[1], nn[1])
        }
        temp = Math.min(tt[1], nn[1]) - Math.max(tt[0], nn[0])
        if (temp >= 0) {
          result.push(n * (temp + 1))
        }
      }
    }
    // 如果没有找到交叉点
    if (start === undefined || end === undefined) {
      if (n < 1 || top[0] === undefined) {
        return false
      } else {
        width = top[0][1] - top[0][0] + 1
        result.push((n - 1) * width)
      }
    } else {
      // 找到交叉点继续下一行
      if (arr.length > 0) {
        arr.push([
          [start, end]
        ])
        maxRect(arr, result, n++)
      } else {
        // 从某一行一直计算到最后一行,这个时候start和end一直有值,所以不会进入到if层,这个时候n就是累计的行数(高),end-start+1就是宽
        result.push(n * (end - start + 1))
      }
    }
  }
  while (matrix.length >= 1) {
    maxRect([].concat(matrix), result)
    matrix.pop()
  }
  // 取最大值
  let max = 0
  let item = result.pop()
  while (item) {
    if (item > max) {
      max = item
    }
    item = result.pop()
  }
  return max
}
// console.log('结果:', maximalRectangle([
//   ['1', '0', '1', '0', '0'],
//   ['1', '0', '1', '1', '1'],
//   ['1', '1', '1', '1', '1'],
//   ['1', '0', '0', '1', '0']
// ])) // 6
// console.log('###################################################')
// console.log('结果:', maximalRectangle([['1']])) // 1
// console.log('结果:', maximalRectangle([['0']])) // 0
// console.log('###################################################')
// console.log('结果:', maximalRectangle([['0', '1'], ['0', '1']])) // 2
// console.log('###################################################')
// console.log('结果:', maximalRectangle([
//   ['1', '0', '1', '1', '1'],
//   ['0', '1', '0', '1', '0'],
//   ['1', '1', '0', '1', '1'],
//   ['1', '1', '0', '1', '1'],
//   ['0', '1', '1', '1', '1']
// ])) // 6
// console.log('###################################################')
// console.log('结果:', maximalRectangle([['0', '1'], ['1', '0']])) // 1
// console.log('###################################################')
console.log('结果:', maximalRectangle([
  ['0', '0', '0', '1', '0', '1'],
  ['0', '1', '1', '0', '0', '1'],
  ['1', '0', '1', '1', '1', '1']
])) // 4
// console.log('###################################################')
console.log('结果:', maximalRectangle([['1'], ['0'], ['1'], ['1'], ['1'], ['1'], ['0']])) // 4

其他小伙伴的解法

let maximalRectangle = matrix => {
  // 转化思路,将矩阵构建成柱形图,就可以用柱形图中的最大矩形来求解;
  // 两步:
  // 构造柱形图:
  // 一层,两层,三层,....n层这样叠加;
  // 以右边示例为例:
  // 一层: [1,0,1,0,0],最大面积为1
  // 二层: [2,0,2,1,1],最大面积为3
  // 三层: [3,1,3,2,2],最大面积为6
  // 四层: [4,0,0,3,0],最大面积为1
  // 这里可以做一个优化,就是不用每次都每一列循环遍历来求高度而是可以利用上一层的高度了来累加
  if (!matrix.length) {
    return 0
  }
  // [1,0,1,0,0],最大面积为1
  let heights = matrix[0].map(item => +item)
  let large = largestRectangleArea(heights)
  for (let i = 1; i < matrix.length; i++) {
    const axis = matrix[i]
    for (let j = 0; j < axis.length; j++) {
      if (matrix[i][j] > 0) {
        heights[j] = heights[j] + 1
      } else {
        heights[j] = 0
      }
    }
    large = Math.max(large, largestRectangleArea(heights))
  }
  return large
}
// 柱形图中的最大矩形的解:栈顶法
let largestRectangleArea = function (heights) {
  let large = 0
  let shift = [{ index: -1, height: -1 }]
  for (let i = 0; i < heights.length; i++) {
    while (shift[shift.length - 1].height !== -1 && shift[shift.length - 1].height > heights[i]) {
      const pop = shift.pop()
      large = Math.max(large, pop.height * (i - 1 - shift[shift.length - 1].index))
    }
    shift.push({ index: i, height: heights[i] })
  }
  const top = shift[shift.length - 1].index
  while (shift[shift.length - 1].height !== -1) {
    const pop = shift.pop()
    large = Math.max(large, pop.height * (top - shift[shift.length - 1].index))
  }
  return large
}

本题类似于:84. 柱状图中最大的矩形 - 力扣(LeetCode)


未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序边界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值