illx10000

青春不是年华,而是心境

单调栈

2019年6月23日 星期天, 发表于 深圳

如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)

单调栈是一种特殊的数据结构,需要保持栈单调,具体可以参考oi-wiki.org

2.2 数据结构

参考 https://blog.csdn.net/liujian20150808/article/details/50752861

  1. 单调栈里的元素具有单调性
  2. 元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除
  3. 使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的元素。
while !sta.empty() && sta.top()<x
    sta.pop()
sta.push(x)

2.3 用途

2.3.1 问题一

建议阅读原文:https://zhuanlan.zhihu.com/p/26465701

Q: 给一个数组,返回一个大小相同的数组。返回的数组的第i个位置的值应当是,对于原数组中的第i个元素,至少往右走多少步,才能遇到一个比自己大的元素(如果之后没有比自己大的元素,或者已经是最后一个元素,则在返回数组的对应位置放上-1)。

A: 其中O(N^2)的解法不赘述,看一下如何使用单调栈来实现:

vector<int> nextExceed(vector<int> &input) {
	vector<int> result (input.size(), -1);
	stack<int> monoStack;
	for(int i = 0; i < input.size(); ++i) {	
		while(!monoStack.empty() && input[monoStack.top()] < input[i]) {
			result[monoStack.top()] = i - monoStack.top();
			monoStack.pop();
		}
		monoStack.push(i);
	}
	return result;
}

2.3.2 leetcode (84)

题目链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram/

首先回想一下单调栈的功能,找到比他大/小的元素;

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) //[2,1,5,6,2,3]
    {
        heights.push_back(0);

        stack<int> stackIndex;
        int maxArea = 0;
        for (size_t i = 0; i < heights.size(); i++)
        { 
            //弹出的元素,说明以他的高度为高的矩形无法向右再扩充面积了
            while (!stackIndex.empty() && heights[stackIndex.top()] >= heights[i]) 
            {
                int height = heights[stackIndex.top()]; //当前矩形的高度
                stackIndex.pop();

                int startPos = (stackIndex.empty()?-1:stackIndex.top() ) + 1; //startPos是第一个位置比heiigth高的位置
                int width  =  i - startPos;
                int area = height * width;

                if( area > maxArea )
                {
                    maxArea = area;
                }
            }
            stackIndex.push(i); //stackIndex内的每一元素单调增加 
        }
        return maxArea;
    }
};

看了答案很久,才看明白。
使用递增的单调栈,如果遇到一个比它小的元素,此时弹出栈中的元素,栈中的元素所有的高度都应该比当前元素要高; 那么以栈中的元素为高的矩形,它的左右边界在哪里呢?

  1. 右边界应该是当前处理的位置的前一个位置,为什么呢?因为当前栈的位置可以一直衍生到当前处理处理的位置(递增);
  2. 左边界应该是当前栈top-1的前一个位置(因为所有的top-1之后的元素都是大于当前栈位置的,不然已经被弹出去了)

3. 总结:

参考这里的建议: https://www.1point3acres.com/bbs/thread-204388-1-1.html

  1. 单调栈何时用: 为任意一个元素找左边和右边第一个比自己大/小的位置,用单调栈
  2. 用递增单调栈还是递减单调栈:递减栈会剔除波谷,留下波峰;递增栈剔除波峰,留下波谷