【单调栈】84. 柱状图中最大的矩形——数据结构经典问题求解思路
该问题属于虽然是困难模式,但是却是数据结构的一个经典问题,该问题的求解方法包括:暴力法、单调栈方法。建议读者先掌握暴力法的求解思路,之后出于优化该算法的目的,提出了单调栈算法。
84. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。
输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10
1、暴力法
我们知道,求矩形的面积需要知道矩形的长和宽,而在该题目中宽度即不同柱形之间距离,长度为柱形的高度。因此,我们可以设计一个算法,使用双层for循环来定一移一,通过观察我们发现可以固定宽度、遍历高度,依次求得所有可能的面积情况。
具体地,我们可以固定某一个柱形,依次往后遍历,每遍历一次,宽度就加一,直到遇到比该柱形高度要低的情况,此时该柱形所能计算的最大面积即等于其高度*宽度。
详细的代码如下(c++):
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
int ans = 0;
// 枚举左边界
for (int left = 0; left < n; ++left) {
int minHeight = INT_MAX;
// 枚举右边界
for (int right = left; right < n; ++right) {
// 确定高度
minHeight = min(minHeight, heights[right]);
// 计算面积
ans = max(ans, (right - left + 1) * minHeight);
}
}
return ans;
}
};
该算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2),在测试中只有java语音编程能够通过,其他均报超时!!!因此我们需要对该算法进行优化处理。
2、单调栈
通过对暴力法求解我们发现,对于后期的计算有大量的重复,因此我们需要来保存之前所计算的内容,在这里我们提出了可以使用栈的方式,每次只需要利用栈顶元素和当前元素来计算面积。
单调栈的定义:栈内的所有元素按照一定的顺序排列,单调栈包括单调递增栈和单调递减栈,顾名思义,栈内元素递增(递减)顺序存放为单调递增(递减)栈。
具体的算法思路:
在原数组的首尾各插入一个0,以此保证栈的完全运行。
逐个遍历数组的元素,若当前元素>=栈顶元素则入栈。
否则就执行以下操作:
弹出栈顶元素,获取下一个栈顶元素
a
a
a,
a
a
a和当前元素的下标差作为宽,弹出的栈顶元素为高,计算面积,并与当前的最大面积相比较。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
// 单调栈
int n = heights.size();
int max_s = 0;
stack<pair<int,int>> increase_st;
heights.push_back(0);
heights.insert(heights.begin(),0);
pair<int,int> a;
a.first = 0;
a.second = 0;
increase_st.push(a);
for(int i = 1;i<=n+1;i++){
pair<int,int> top = increase_st.top();
while(top.second>heights[i]){
increase_st.pop();
int temp_heigth = top.second;
top = increase_st.top();
max_s = max(max_s,temp_heigth*(i-top.first-1));
}
pair<int,int> temp;
temp.first = i;
temp.second = heights[i];
increase_st.push(temp);
}
return max_s;
}
};
在使用单调栈后,算法的时间复杂度为 O ( n ) O(n) O(n),性能大大提高,正是借助了栈这一数据结构,使得算法更好的优化。在求解问题中,我们通常最先考虑使用的仍然是暴力法,但是由于暴力法的时间复杂度问题,这就不得不要求我们考虑优化方案。