直方图最大矩形

经评论指出,原文中的枚举和动态规划都有一些问题,博主已经于2019年12月31日晚上对原文进行了更正。

问题

给定直方图,求直方图中最大的矩形面积。

例如下图,可以用数组表示为[2,1,5,6,2,3]。

这里写图片描述

对应的最大矩形面积为10.

这里写图片描述

枚举

对于直方图中每根柱子,假定其是当前矩形中最短的一根,分别向左右两个方向搜索,直到遇到比它短的为止。

int h[] = {2,1,5,6,2,3};
int length = 6, max = 0, s = 0;

for(int i = 0; i < length; i ++) {
	int k, j;
	for(k = i - 1; k >= 0; k --) {
		if (h[i] > h[k]) break;			//找到左边界
	}
	for(j = i + 1; j < length; j ++) {
		if (h[i] > h[j]) break;           //找到右边界
	}
	s = h[i] * (j - k - 1);
	if (max < s) max = s;
}

枚举的时间为复杂度为O(n2),空间复杂度为O(1)。

动态规划

用动态规划来考虑这个问题。动态规划必须找到一个可以在相邻柱状条之间转换的状态描述。相邻柱状条除了比大小,再没有别的关系可用了。

所以,定义left[i]表示往左边扩展,高度不低于h[i]的连续柱状条中最远的下标。

right[i]表示往右边扩展,高度不低于h[i]的连续柱状条中最远的下标。

这样,以h[i]为高度的最大矩形面积为h[i]*(right[i] - left[i]+1)。

枚举所有位置,就可以找到最大矩形了,而且一定可以找到。因为直方图中的最大矩形,一定有一条最低的柱状条。

参考代码如下:

int h[] = {2,1,5,6,2,3};
int length = 6, max = 0, s = 0;
int left[6] = {0}, right[6] = {0};

left[0] = 0;
for(int i = 1; i < length;i ++) {
	int k = i;
	while(k > 0 && h[i] <= h[k-1]) {
		k = left[k-1];
	}
	left[i] = k;
}

right[length-1] = length - 1;
for(int i = length - 2; i >= 0; i --) {
	int k = i;
	while(k < length-1 && h[i] <= h[k+1]) {
		k = right[k+1];
	}
	right[i] = k;
}

for(int i = 0; i < length; i ++) {
	s = h[i] * (right[i] - left[i] + 1);
	if (max < s) max = s;
}

此处,与枚举比起来,动态规划采用的计算思路是一样的,但仅仅加速了left和right数组的计算,并没有从实质上改变其复杂度。所以,动态规划时间复杂度为O(n2),空间复杂度为O(n)。

网络上另外一种动态规划的思路是,定义dp[i][j]表示从下标i到下标j的最大矩形面积,对图形组合进行分析,有如下:

dp[i][j] = max(dp[i-1][j], dp[i][j+1], 最短柱条乘以个数(即j-i+1))

该公式中最后一项的计算仍然需要遍历,并没有从实质上改进复杂度。

单调栈

网上广为流传的是一种利用单调栈思路的代码。基本原理描述如下:

扫描数组,并维护一个单调栈,栈里面的元素是递增的。如果当前元素比栈顶元素大,入栈。否则,出栈,直到当前元素大于栈顶元素。每次出栈时,计算以当前栈顶元素为高度的最大矩形。

代码如下:

vector<int> h = {2,1,5,6,2,3};
int length = 6, max = 0;
stack<int> s;

h.push_back(0);			//哨兵,用于清空单调栈
for(int i = 0; i < length; i ++) {
	while(!s.empty() && h[i] <= h[s.top()]) {		
		int cur = s.top();
		s.pop();
		int r = h[cur] * (s.empty() ? i : i - s.top() - 1);
		max = (max < r : r : max);
	}
	s.push(i);
}

该方法的时间复杂度是O(n),空间复杂度也为O(n)。

单调栈的作用,在于保存了每个柱状条往前比自己高的元素信息。每个出栈元素,必定是大于当前元素的。因而,其构成的最大矩形,也只能到当前元素为止。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值