直方图最大矩形

问题

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

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

直方图最大矩形_第1张图片

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

直方图最大矩形_第2张图片

枚举

对每个左边界,可以枚举其右边界的位置,寻找面积最大值。

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

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

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

动态规划

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

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

right[i]表示往右边扩展,高度不低于h[i]的连续柱状条个数。

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

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

参考代码如下:

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

for(int i = 1; i < length;i ++) 
    left[i] = (h[i] <= h[i-1] ? left[i-1] + 1 : 0);

for(int i = length - 2; i >= 0; i --) 
    right[i] = (h[i] <= h[i+1] ? right[i+1] + 1: 0);

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

动态规划时间复杂度为O(n),空间复杂度也为O(n)。

单调栈

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

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

代码如下:

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)。

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

你可能感兴趣的:(算法)