单调栈经典问题直方图最大面积

单调栈是一个很好用的工具,可以高效的将一些 O ( n 2 ) O(n^2) O(n2)的问题优化到 O ( n ) O(n) O(n) 经典问题如:POJ:2559 Largest Rectangle in a Histogram。如下直方图,每个直方柱的宽度为1,高度如下所示,请问由连续直方柱构成的最大矩形面积?
在这里插入图片描述
O ( n 2 ) O(n^2) O(n2)的算法很容易想到,因为最终的矩形一定等于某个直方柱的高度,因此尝试每一个直方柱的高度 h i h_i hi向左右两侧扩展,扩展的截止条件是两侧第一个低于 h i h_i hi的高度为止。

当然我们对 O ( n 2 ) O(n^2) O(n2)的算法是不满意的,因此我们可以构建单调栈。我们发现,如果直方柱的高度是单调递增的话,那么以每一个柱子为高度的矩形面积都可以向右持续增加。如下图所示,这种情况下我们可以将这些柱子依次存入栈中。

如下图所示,如果第9个直方柱的高度为11,显然破坏了单调性,同时我们发现高度为16、14、12的柱子都无法再往右边扩展面积。为此为了维护单调性,我们做以下操作:
  1. 取出8号高度为16的柱子,可以算出以8号柱子为高度的矩形最大面积为16×1 = 16
  2. 取出7号高度为14的柱子,可以算出以7号柱子为高度的矩形最大面积为14×2 = 28
  3. 取出6号高度为12的柱子,可以算出以6号柱子为高度的矩形最大面积为12×3 = 36

这里需要注意的是,我们怎么知道上述高度为16,14,12几个高度要分别乘1,2,3,实际上我们在栈中存储每个直方柱的下标即可。然后可以简单的根据下标计算要出栈的柱子跟当前遍历到的柱子的距离(以出栈柱子为高度可以向右扩展的最远处)

接下来,我们发现9号柱子已经超过5号柱子,因此已经单调了,那么将9号柱子存入栈中。
如此往复,但是我们发现要计算某个高度的最大矩形面积,接下来需要处理两个问题:

1)如果所有柱子的高度是递增,那么如何计算面积? 因为全部递增,我们只会一直push,一直push就不会计算面积。为了使模型简化,我们可以在最末尾加一个高度为0(或-1)的直方柱,使得其对与前面的任何柱子都是逆序,因此可以取出前面所有的柱子并计算面积。

2)如果所有柱子的高度是递减,那么如何计算面积? 如下所示,首先1号柱子入栈,当遍历2号柱子时,1号柱子出栈,它跟2号柱子的距离为1,因此以16为高度的最大矩形面积为:16×1 = 16, 然后2号柱子进栈,接下来遍历3号柱子,它右跟栈顶的2号柱子构成逆序,因此2号柱子出栈,那么2号柱子虽然无法向右扩展,但可以一直向左扩展到最左侧,那么这里需要特殊处理,如果某个柱子出栈后栈空,说明该柱子之前的柱子都比它高,否则不会就它一个在栈里,回顾单调栈的意义。因此,这种情况只需要将柱子高度乘以柱子下标即可,因此以2号柱子为高度的最大矩形面积为14×2=28。依次类推,以3号柱子为高度的最大矩形面积为13*3=36… 或者更简单的方法,开始先push一个高度为0的柱子,这样最简单。

我们发现每个柱子进栈一次出栈一次,因此复杂度为$O(n)$。最后,上代码:
int s[N] = { 06, 2, 5, 4, 5, 1, 6, 0 },maxArea; //从1开始
int GetMaxArea(){
    stack<int> S; S.push(0);
    for (int i = 1; i<N; i++){
        if (S.empty()) S.push(i);   //栈为空,直接进栈
        else if (s[S.top()] <= s[i]) S.push(i);  //栈中元素<当前元素,直接进入
        else{
            while (!S.empty() && s[S.top()] > s[i]){ //不为空且大于当前元素
                int index = S.top(), area = 0;
                S.pop();
                if (!S.empty()) area = s[index] * (i - index);
                else area = s[index] * index;
                maxArea = max(maxArea,area);
            }
            S.push(i);
        }
    }
    return maxArea;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Researcher-Du

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值