深圳幻海软件技术有限公司 欢迎您!

【leetcode热题100】接雨水、直方图最大矩形面积、矩阵中最大的矩形

2023-05-23

文章目录一、接雨水方法一:按列求(动态规划)方法二:双指针方法三:单调栈二、直方图最大矩形面积单调栈哨兵位优化三、矩阵中最大的矩形前缀和+单调栈一、接雨水题目链接题目描述:给定n个非负整数表示每个宽度为1的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。示例1:输入:height=[0,1,

文章目录

  • 一、接雨水
    • 方法一:按列求(动态规划)
    • 方法二:双指针
    • 方法三:单调栈
  • 二、直方图最大矩形面积
    • 单调栈
    • 哨兵位优化
  • 三、矩阵中最大的矩形
    • 前缀和+单调栈

一、接雨水

题目链接

题目描述:

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

方法一:按列求(动态规划)

我们把每一列能接的水加起来即可,而每一列的水只取决于当前列左边的最高墙和右边的最高墙
根据木桶效应,在左边和右边取低的那一个,然后减去当前列的高度即可求出当前列的接水量。
这样我们就可以创建两个数组,一个记录包括当前列左边的最高墙,一个记录包括当前列右边的最高墙,采用动态规划的思想。
为什么要包括当前列?

首先可以解决边界问题,其次如果当前列是左边最高的或者右边最高的,那么减去自己的高度就是0。

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        vector<int> leftMax(n), rightMax(n);
        int Max = 0;
        for(int i = 0; i < n; i++)
        {
            if(height[i] > Max)
            {
                Max = height[i];
            }
            leftMax[i] = Max;
        }
        Max = 0;
        for(int i = n - 1; i >= 0; i--)
        {
            if(height[i] > Max)
            {
                Max = height[i];
            }
            rightMax[i] = Max;
        }
        int sum = 0;
        for(int i = 0; i < n; i++)
        {
            sum += min(leftMax[i], rightMax[i]) - height[i];
        }
        return sum;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

方法二:双指针

上面我们说过只用看左右两边最高墙中的最小值即可,所以现在我们定义两个指针,left从左向右,right从右向左,每次让小的那一列墙的指针向中间移动(因为我们不关心左边最高墙和右边最高墙中的高的那列墙)。

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        int leftMax = 0, rightMax = 0;
        int left = 0, right = n - 1;
        int sum = 0;
        while(left <= right)
        {
            if(height[left] < height[right])
            {
                leftMax = max(leftMax, height[left]);
                sum += leftMax - height[left];
                left++;
            }
            else
            {
                rightMax = max(rightMax, height[right]);
                sum += rightMax - height[right];
                right--;
            }
        }
        return sum;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

方法三:单调栈

用一个极端场景来举例子:

如果是单调递减的就依次入栈,直到碰到比栈顶元素要高的墙,此时就依次判断前面入栈且比当前墙要低的元素。

比方说现在到5下标。此时依次出栈之前的元素来计算接水量:

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        stack<int> st;
        int sum = 0;
        for(int i = 0; i < n; i++)
        {
            while(!st.empty() && height[i] > height[st.top()])
            {
                int cur = st.top();
                st.pop();
                if(st.empty())
                {
                    break;
                }
                int len = i - st.top() - 1;
                sum += (min(height[st.top()], height[i]) - height[cur]) * len;
            }
            st.push(i);
        }
        return sum;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

二、直方图最大矩形面积

题目链接

题目描述:

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

示例 1:

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

输入: heights = [2,4]
输出: 4

思路分析:

单调栈

这道题可以类比上面的接雨水问题,我们也可以用单调栈的方式来求解。
栈里面存的是下标,主要是用来计算宽度。
遍历每个下标,如果当前列大于栈顶元素,就继续入栈,如果小于栈顶元素,进行处理:

从1开始遍历,当遍历到1的位置时还不能确定1位置是不是最大的矩形面积,继续向后遍历,当遍历到2的位置时,就可以确定2之前的的大面积了。

如果已经确定了一个柱形的高度,我们可以无视它(出栈)。

为什么可以无视呢?
后边的元素一定比1要小,所以当要求最大面积的时候一定会跨过第一列:

就算1前面也有元素也是可以可以直接跨过。
可以看到2不用关注1,3不用关注1和2。

这里还有两个特殊的情况:
1️⃣ 弹栈的时候,栈为空;
2️⃣ 遍历完成以后,栈中还有元素;

如果弹栈的时候栈为空,那么说明宽度要从当前位置一直延伸到0下标。
如果遍历完后栈中还有元素。比如说这个图:

最后剩下的就是4和6,对于4和6,先处理6在处理4:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        stack<int> st;
        int ans = 0;
        for(int i = 0; i < n; i++)
        {
            while(!st.empty() && heights[i] < heights[st.top()])
            {
                int mid = st.top();
                st.pop();
                int w = i;
                if(!st.empty())
                {
                    w = i - st.top() - 1;
                }
                ans = max(ans, w * heights[mid]);
            }
            st.push(i);
        }
        while(!st.empty())
        {
            int mid = st.top();
            st.pop();
            int w = n;
            if(!st.empty())
            {
                w = n - st.top() - 1;
            }
            ans = max(ans, w * heights[mid]);
            cout << w * heights[mid] << endl;
        }
        return ans;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

哨兵位优化

为了处理上面两个特殊情况,我们可以在首位都添加一个0。

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        heights.insert(heights.begin(), 0);
        heights.push_back(0);
        int n = heights.size();
        stack<int> st;
        st.push(0);
        int ans = 0;
        for(int i = 1; i < n; i++)
        {
            while(heights[i] < heights[st.top()])
            {
                int mid = st.top();
                st.pop();
                int left = st.top();
                int right = i;
                int w = right - left - 1;
                ans = max(ans, w * heights[mid]);
            }
            st.push(i);
        }
        return ans;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

三、矩阵中最大的矩形

题目链接

题目描述:

给定一个由 0 和 1 组成的矩阵 matrix ,找出只包含 1 的最大矩形,并返回其面积。
注意:此题 matrix 输入格式为一维 01 字符串数组。

示例 1:

输入:matrix = [“10100”,“10111”,“11111”,“10010”]
输出:6
解释:最大矩形如上图所示。

示例 2:

输入:matrix = []
输出:0

示例 3:

输入:matrix = [“0”]
输出:0

示例 4:

输入:matrix = [“1”]
输出:1

示例 5:

输入:matrix = [“00”]
输出:0

思路分析:
例如上面的图,我们可以分层看,每一层都是求直方图最大矩形面积。

第一层柱状图的高度[“1”,“0”,“1”,“0”,“0”],最大面积为1;
第二层柱状图的高度[“2”,“0”,“2”,“1”,“1”],最大面积为3;
第三层柱状图的高度[“3”,“1”,“3”,“2”,“2”],最大面积为6;
第四层柱状图的高度[“4”,“0”,“0”,“3”,“0”],最大面积为4;

这道题的解法就是前缀和+单调栈

前缀和+单调栈

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        stack<int> st;
        st.push(0);
        int ans = 0;
        for(int i = 1; i < n; i++)
        {
            while(heights[i] < heights[st.top()])
            {
                int mid = st.top();
                st.pop();
                int left = st.top();
                int right = i;
                int w = right - left - 1;
                ans = max(ans, w * heights[mid]);
            }
            st.push(i);
        }
        return ans;
    }

    int maximalRectangle(vector<string>& matrix) {
        if(matrix.size() == 0) return 0;
        int res = 0;
        vector<int> v(matrix[0].size() + 2);
        for(int i = 0; i < matrix.size(); i++)
        {
            for(int j = 0; j < matrix[i].size(); j++)
            {
                if(matrix[i][j] == '1')
                {
                    v[j + 1] += 1;
                }
                else
                {
                    v[j + 1] = 0;
                }
            }
            res = max(res, largestRectangleArea(v));
        }
        return res;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45


文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览46879 人正在系统学习中