LeetCode 84 & 85.柱状图最大矩形与最大矩形

LeetCode 84 & 85.柱状图最大矩形与最大矩形

1. 引言

LeetCode 84 题「柱状图中最大的矩形」是经典的单调栈应用题,而 85 题「最大矩形」则是它的二维扩展------在 0/1 矩阵中找出只包含 1 的最大矩形。85 题的常用解法正是将矩阵逐行转化为柱状图的高度,然后对每一行调用 84 题的解法。本文将详细解释这两道题的代码,并梳理它们之间的联系。


2. LeetCode 84:柱状图中最大的矩形

2.1 题目描述

给定 n 个非负整数表示柱状图中每个柱子的高度,每个柱子宽度为 1,求该柱状图中能勾勒出的最大矩形面积。

2.2 解题思路

使用单调栈,对每个柱子找到其左右两边第一个比它矮的柱子。

  • left[i]:左边第一个高度小于 h[i] 的柱子的下标(若不存在则为 -1)
  • right[i]:右边第一个高度小于 h[i] 的柱子的下标(若不存在则为 n)
    则当前柱子能形成的最大矩形面积为 h[i] * (right[i] - left[i] - 1)
    遍历所有柱子取最大值即可。

2.3 代码实现

cpp 复制代码
class Solution {
public:
    int largestRectangleArea(vector<int>& h) {
        int n = h.size();
        vector<int> left(n), right(n);
        stack<int> st;

        // 计算左边界
        for(int i = 0; i < n; i++) {
            while(!st.empty() && h[st.top()] >= h[i]) st.pop();
            left[i] = st.empty() ? -1 : st.top();
            st.push(i);
        }

        // 清空栈,计算右边界
        while(!st.empty()) st.pop();  // 或 st = stack<int>();

        for(int i = n-1; i >= 0; i--) {
            while(!st.empty() && h[st.top()] >= h[i]) st.pop();
            right[i] = st.empty() ? n : st.top();
            st.push(i);
        }

        int res = 0;
        for(int i = 0; i < n; i++) {
            res = max(res, h[i] * (right[i] - left[i] - 1));
        }
        return res;
    }
};

2.4 代码解释

  • 单调栈维护 :栈中存放柱子的下标,对应的高度保持严格递增(实际弹出条件是 ≥,保证栈内高度递增)。
  • 左边界计算:从左向右遍历,弹出所有高度 ≥ 当前柱子的栈顶,剩下的栈顶就是左边第一个更矮的柱子,若栈空则左边界为 -1。
  • 右边界计算:从右向左遍历,同理。
  • 面积计算:对每个柱子计算可能的最大面积并更新答案。

2.5 复杂度分析

  • 时间复杂度:O(n),每个元素入栈、出栈各一次。
  • 空间复杂度:O(n),栈和左右数组。

3. LeetCode 85:最大矩形

3.1 题目描述

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

3.2 解题思路

将矩阵的每一行作为底边,向上累积连续 1 的个数 ,得到该行的高度数组。

例如,height[i][j] 表示从第 i 行向上(包括 i 行)连续 1 的个数,若 matrix[i][j] == 0 则高度归零。

这样,以第 i 行为底边的所有可能矩形就等价于一个柱状图,其柱子高度为 height[i][j]

对每一行调用 84 题的 largestRectangleArea,取所有行的最大值即为答案。

3.3 代码实现

cpp 复制代码
class Solution {
public:
    // 复用 84 题的代码
    int largestRectangleArea(vector<int>& h) {
        int n = h.size();
        vector<int> left(n), right(n);
        stack<int> st;

        for(int i = 0; i < n; i++) {
            while(!st.empty() && h[st.top()] >= h[i]) st.pop();
            left[i] = st.empty() ? -1 : st.top();
            st.push(i);
        }

        while(!st.empty()) st.pop();

        for(int i = n-1; i >= 0; i--) {
            while(!st.empty() && h[st.top()] >= h[i]) st.pop();
            right[i] = st.empty() ? n : st.top();
            st.push(i);
        }

        int res = 0;
        for(int i = 0; i < n; i++) {
            res = max(res, h[i] * (right[i] - left[i] - 1));
        }
        return res;
    }

    int maximalRectangle(vector<vector<char>>& matrix) {
        if(matrix.empty() || matrix[0].empty()) return 0;
        int m = matrix.size(), n = matrix[0].size();

        // 构造高度数组
        vector<vector<int>> h(m, vector<int>(n, 0));
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(matrix[i][j] == '1') {
                    h[i][j] = (i == 0) ? 1 : h[i-1][j] + 1;
                } // 否则保持 0
            }
        }

        int res = 0;
        for(int i = 0; i < m; i++) {
            res = max(res, largestRectangleArea(h[i]));
        }
        return res;
    }
};

3.4 代码解释

  1. 高度数组 hh[i][j] 记录第 i 行第 j 列向上连续 1 的个数。若当前为 '1',则累加上一行的高度(如果 i=0 则直接为 1);若为 '0' 则高度置 0。
  2. 逐行调用 84 题函数 :对每一行的 h[i](一维数组)计算柱状图最大矩形面积,并更新全局最大值。
  3. 为什么可以直接调用 84 题:每一行的高度数组正好对应一个柱状图,矩形必须连续且高度受限于区间内的最小高度,这与 84 题完全一致。

3.5 复杂度分析

  • 时间复杂度:O(m×n)。构造高度数组需要 O(m×n),每行调用一次 84 题的 O(n) 算法,共 m 行,总 O(m×n)。
  • 空间复杂度:O(m×n) 存储高度数组,但可以优化为 O(n) 滚动数组(此处未优化)。

4. 总结:两题的联系与区别

题目 核心思想 关键点
84 单调栈求每个柱子左右第一个更矮的位置 一维问题,不可重排
85 将二维矩阵逐行转化为一维柱状图,每行调用 84 题 二维问题,利用高度累积,不可重排

联系 :85 题的核心技巧是高度累积,把二维问题降为一维,然后复用 84 题的高效解法。这种思路在矩阵类问题中非常常见(如统计全 1 正方形等)。

区别:84 题是基础算法题,85 题是它的应用扩展。掌握 84 题的单调栈解法,是理解 85 题的前提。

相关推荐
We་ct2 小时前
LeetCode 427. 建立四叉树:递归思想的经典应用
前端·算法·leetcode·typescript·dfs·深度优先遍历·分治
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- 补充包】
开发语言·前端·数据结构·数据库·c++·算法·蓝桥杯
夏乌_Wx2 小时前
Linux 进程间通信 IPC 总结:管道 + 信号量 + 共享内存 + 消息队列(附代码)
linux·数据结构·算法
Tisfy2 小时前
LeetCode 1878.矩阵中最大的三个菱形和:斜向前缀和
算法·leetcode·矩阵
budingxiaomoli2 小时前
优选算法-队列+宽搜
算法
张李浩10 小时前
Leetcode 054螺旋矩阵 采用方向数组解决
算法·leetcode·矩阵
big_rabbit050210 小时前
[算法][力扣101]对称二叉树
数据结构·算法·leetcode
美好的事情能不能发生在我身上11 小时前
Hot100中的:贪心专题
java·数据结构·算法