想要精通算法和SQL的成长之路 - 柱状图中最大的矩形

想要精通算法和SQL的成长之路 - 柱状图中最大的矩形

前言

想要精通算法和SQL的成长之路 - 系列导航

一. 柱状图中最大的矩形

原题链接

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。

在这里插入图片描述
这道题目我们可以仿照着接雨水这道题目来做。

思路:

  1. 我们可以遍历所有的柱子,在每次遍历的时候,我们以当前柱子作为一个中心点。
  2. 我们分别向左、向右各自寻找第一个小于当前高度的柱子,找到他们的索引分别是leftright
  3. 那么以当前柱子为固定高度的最大面积就是 :(right-left-1)* curHeight

那么我们来看下题目给的案例,按按照这个思想来做是否可行呢?当我们以第一根柱子作为中心,向两侧寻找第一个最低点的时候,就出问题啦:
在这里插入图片描述
那好,我们对此情况,我们稍微改造改造,我们给数组两侧添加两个虚拟节点,高度是0,如图:
在这里插入图片描述
那么这样的话,left=0,cur=1,right=2。以高度为2去寻找最大面积的话,就是2*(2-0-1)=2了。

我们再来看下以柱子高度5的为中心:
在这里插入图片描述

我们在试想一下,既然我们要以每个遍历的节点为中心,并寻找到左右两侧第一个比他小的元素。那么我们就可以使用单调递增栈来完成。

前期准备部分,我们先给数组添加两个虚拟节点

int[] tmpHeight = new int[heights.length + 2];
for (int i = 1; i <= heights.length; i++) {
    tmpHeight[i] = heights[i - 1];
}

然后我们再看看递归过程:

  • 既然我们需要单调递增,那么遇到小的,就应该把当前栈内比当前高度高的,给剔除(同时计算高度)。也就保证了循环:while (!stack.isEmpty()&&tmpHeight[stack.peek()]>tmpHeight[right])因为无论怎么样,我们必须要把当前元素给放到栈中的。不能不放。
  • 既然是单调递增栈,那么栈顶元素和栈中的第二个元素就是我们要的中心元素、左侧第一个比栈顶元素小的。而当前元素就是右侧第一个比栈顶元素小的。看图能更直观点(红框部分),这时候遍历的时候,栈中元素有0和2,当遇到1的时候,满足while条件。
    在这里插入图片描述
for (int right = 0; right < tmpHeight.length; right++) {
    // 一旦遇到某个节点比当前节点小了,就可以计算面积了。
    while (!stack.isEmpty() && tmpHeight[stack.peek()] > tmpHeight[right]) {
        // 栈顶元素(也就是我们说的中心柱子)
        int current = stack.pop();
        // left是左侧第一个比中心柱子矮的,right就是右侧第一个比中心柱子高的,
        // 因为在tmpHeight[stack.peek()] > tmpHeight[right]的前提约束下
        Integer left = stack.peek();
        // 计算面积
        res = Math.max(res, (right - left - 1) * tmpHeight[current]);
    }
    stack.push(right);
}

最终代码如下:

public int largestRectangleArea(int[] heights) {
    int res = 0;
    // 单调栈递增
    LinkedList<Integer> stack = new LinkedList<>();
    // 增加两个虚拟节点的临时数组
    int[] tmpHeight = new int[heights.length + 2];
    for (int i = 1; i <= heights.length; i++) {
        tmpHeight[i] = heights[i - 1];
    }
    for (int right = 0; right < tmpHeight.length; right++) {
        // 一旦遇到某个节点比当前节点小了,就可以计算面积了。
        while (!stack.isEmpty() && tmpHeight[stack.peek()] > tmpHeight[right]) {
            // 栈顶元素(也就是我们说的中心柱子)
            int current = stack.pop();
            // left是左侧第一个比中心柱子矮的,right就是右侧第一个比中心柱子高的,
            // 因为在tmpHeight[stack.peek()] > tmpHeight[right]的前提约束下
            Integer left = stack.peek();
            // 计算面积
            res = Math.max(res, (right - left - 1) * tmpHeight[current]);
        }
        stack.push(right);
    }
    return res;
}

二. 最大矩形

原题链接
在这里插入图片描述
思路:

  1. 我们将这个矩阵拆分成多行柱状图(第一题)
  2. 我们根据行来从上往下遍历,将每一列当做是一列柱状图。如果是数字1那么高度加1,。倘若遇到0,那么高度重置为0。
  3. 从上往下遍历的时候,分别计算:一行数组组成的柱状图、二行数组组成的柱状图、三行…下对应的最大面积。取最大数即可。

代码如下:

public int maximalRectangle(String[] matrix) {
    // 判空
    if (matrix.length == 0) {
        return 0;
    }
    int rows = matrix.length;
    // 列个数
    int cols = matrix[0].length();
    int[] arr = new int[cols];
    int res = 0;
    // 逐行计算
    for (int i = 0; i < rows; i++) {
        char[] charArray = matrix[i].toCharArray();
        for (int j = 0; j < cols; j++) {
            // 如果是1,就在原来的基础上加1,否则就是0
            arr[j] = charArray[j] == '1' ? arr[j] + 1 : 0;
        }
        // 计算当前行的最大矩形面积
        int tmpMax = largestRectangleArea(arr);
        // 更新最大值
        res = Math.max(res, tmpMax);
    }
    return res;
}
// 第一题完整代码
public int largestRectangleArea(int[] heights) {
    int res = 0;
    // 单调栈递增
    LinkedList<Integer> stack = new LinkedList<>();
    // 增加两个虚拟节点的临时数组
    int[] tmpHeight = new int[heights.length + 2];
    for (int i = 1; i <= heights.length; i++) {
        tmpHeight[i] = heights[i - 1];
    }
    for (int right = 0; right < tmpHeight.length; right++) {
        // 一旦遇到某个节点比当前节点小了,就可以计算面积了。
        while (!stack.isEmpty() && tmpHeight[stack.peek()] > tmpHeight[right]) {
            int current = stack.pop();
            Integer left = stack.peek();
            res = Math.max(res, (right - left - 1) * tmpHeight[current]);
        }
        stack.push(right);
    }
    return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Zong_0915

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

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

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

打赏作者

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

抵扣说明:

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

余额充值