柱状图中最大的矩形

题目

Alt
Alt
题目来源https://leetcode-cn.com/problems/largest-rectangle-in-histogram/

解题思路

开始的思路

一开始我是这样想的:先遍历一遍数组,找到最短的那条边,求出以最短边为高的矩形面积(既然是最短边,那么它的底肯定是数组长度),然后再分别求出最短边的左边和右边的最大矩形面积,这又可以归结为同样的问题,那么就可以利用分治法解决,最后在这三者的值中选最大的那个。
所以这道题可以利用分治法解决,但是在实际编程的过程中,遇到几个问题:
第一个是如何将最短边的左边和右边的数组分离出来,也就是说如何表示最短边左边和右边的数组,其次,在某种极端情况下,如该数组是一个单调递增/递减的数组,那么这个时间效率不是很好

利用单调栈解决问题

主要参考下篇博客:https://blog.csdn.net/Zolewit/article/details/88863970
首先我们先来理解一下什么是单调栈

单调栈

定义:单调栈是一种特殊的栈,其栈内元素都保存着单调的特性,单调递增或者单调递减,一般是这样子定义的:

stack<int> s;
具体解决思路

首先,我们做这道题如何利用栈来解题呢?我们首先明确一个问题,那就是在几条边组成的矩形中,这个矩形的高永远是最短的一条。
好了,那么我们开始对问题进行分析,要求能描绘的矩形面积最大,首先会有很多情况,比如有可能是连续的几条最高的边能描绘成最大的矩形,最大矩形也有可能是以最短边为高的矩形,所以这道题应该不能用类似于直接定位到某两个点,这两个点构成的矩形面积最大,所以应该还是得遍历一遍数组,在遍历的过程中不断改变最大矩形的面积。
一个思路就是求出以每条边为高的矩形所能构成的最大面积,这样在其中选出最大值即可,那么这个思路有什么难点呢?最主要要解决的问题就是:我如何能知道后边的边是否可以纳入我这个矩形呢,我们肯定不可能未卜先知,所以还是得遍历到才能知道是不是可以用这条边。那么就转换成了解决这样的矛盾:此时我这条边能和多少条边构成的矩形面积最大?
我们还需要知道的是,一条边能和其他边构成最大矩形面积,那么其他边肯定比此边大或者相等,那么这个就跟我们单调栈的性质联系上了。
所以,我们是这样子解决问题的:
首先为了避免陷入异常,我们需要在数组的最末端设置一个最短边,这样做的目的是防止因为数组是单调递增的情况而求不出结果(因为如果数组是单调递增的话,那么程序就会一直压栈,最后当遍历完所有的数组元素,则直接退出,此时栈内保持全部的数组元素,程序返回的是表示矩形最大面积的初始值)

heights.push_back(0);

接着我们就需要设置单调栈:

stack<int> s;

然后就是开始遍历

for (int i = 0; i < size; i++) {
			while (!s.empty() && heights[s.top()] > heights[i]) {
				int h = heights[s.top()];
				s.pop();
				result = max(result, h * (s.empty() ? i : (i - s.top() - 1)));
			}
			s.push(i);
		}

当栈顶元素比遍历到的边大时,则开始弹出栈顶元素,并开始计算以该条边作为高的矩形面积(因为在栈中是一个单调递增的情况,然后又遇到比自己短的边,故直接弹出计算),那么这个矩形的底如何求呢?这里又分两种情况:

s.empty() ? i : (i - s.top() - 1)

如果栈已经空了,那么说明此时以栈顶为高的矩形的底就是i(我们压入栈中的是下标,而不是元素值),否则底边就是i - s.top() - 1(为什么还要再减1呢,因为栈顶元素已经弹出来了,此时已经后移了一个位置),完整代码如下:

int largestRectangleArea(vector<int>& heights) {
        heights.push_back(0);
		int size = heights.size();
		int result = 0;
		stack<int> s;
		for (int i = 0; i < size; i++) {
			while (!s.empty() && heights[s.top()] > heights[i]) {
				int h = heights[s.top()];
				s.pop();
				result = max(result, h * (s.empty() ? i : (i - s.top() - 1)));
			}
			s.push(i);
		}
		return result;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值