文章目录
- 一、接雨水
- 方法一:按列求(动态规划)
- 方法二:双指针
- 方法三:单调栈
- 二、直方图最大矩形面积
- 单调栈
- 哨兵位优化
- 三、矩阵中最大的矩形
- 前缀和+单调栈
一、接雨水
题目链接
题目描述:
给定 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;}
};
方法二:双指针
上面我们说过只用看左右两边最高墙中的最小值即可,所以现在我们定义两个指针,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;}
};
方法三:单调栈
用一个极端场景来举例子:
如果是单调递减的就依次入栈,直到碰到比栈顶元素要高的墙,此时就依次判断前面入栈且比当前墙要低的元素。
比方说现在到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;}
};
二、直方图最大矩形面积
题目链接
题目描述:
给定非负整数数组 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;}
};
哨兵位优化
为了处理上面两个特殊情况,我们可以在首位都添加一个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;}
};
三、矩阵中最大的矩形
题目链接
题目描述:
给定一个由 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;}
};