单调栈
转载自:https://blog.csdn.net/zuzhiang/article/details/78134247
定义:
单调栈,顾名思义,是栈内元素保持一定单调性(单调递增或单调递减)的栈。既然是栈,就满足后进先出的特点。与之相对应的是单调队列。
单调栈有以下两个性质:
1、若是单调递增栈,则从栈顶到栈底的元素是严格递增的。若是单调递减栈,则从栈顶到栈底的元素是严格递减的。
2、越靠近栈顶的元素越后进栈。
3、利用单调栈,可以找到从左/右遍历第一个比它小/大的元素的位置
从当前元素a[i]开始向左边找第一个比a[i]小的值/
for (int i = 0; i < n; i++) { while (!p.empty() && a[p.top()] >= a[i])//按照大于等于a[i]的规则,从a[i]开始,向左边可以延伸最远的数的下标 p.pop(); if (p.empty())//说明a[i]左边的所有数都大于等于a[i] l[i] = 0; else//找到就记录从a[i]开始向左边寻找大于等于a[i]的最大边界下标 l[i] = p.top() + 1; p.push(i); }
从当前元素a[i]开始向右边找第一个比a[i]小的值
while (!p.empty())//记得清空栈 p.pop(); for (int i = n - 1; i >= 0; i--)//往右边找第一个比a[i]小的数 { while (!p.empty() && a[p.top()] >= a[i]) p.pop(); if (p.empty()) r[i] = n - 1; else r[i] = p.top() - 1; p.push(i); }
实现:
例如实现一个单调递增的栈,比如现在有一组数10,3,7,4,12。从左到右依次入栈,则如果栈为空或入栈元素值小于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素小的元素全部出栈。单调递减的栈反之。
10入栈时,栈为空,直接入栈,栈内元素为10。
3入栈时,栈顶元素10比3大,则入栈,栈内元素为10,3。
7入栈时,栈顶元素3比7小,则栈顶元素出栈,此时栈顶元素为10,比7大,则7入栈,栈内元素为10,7。
4入栈时,栈顶元素7比4大,则入栈,栈内元素为10,7,4。
12入栈时,栈顶元素4比12小,4出栈,此时栈顶元素为7,仍比12小,栈顶元素7继续出栈,此时栈顶元素为10,仍比12小,10出栈,此时栈为空,12入栈,栈内元素为12。
伪代码
/* * 本伪代码对应的是单调递减栈 *共n个元素,编号为0~n-1 */ while(栈为空) 栈顶元素出栈; //先清空栈 a[n]=-1; for(i=0;i<=n;i++) { if(栈为空或入栈元素大于等于栈顶元素) 入栈; else { while(栈非空并且栈顶元素大于等于入栈元素) { 栈顶元素出栈; 更新结果; } 将最后一次出栈的栈顶元素(即当前元素可以拓展到的位置)入栈; 更新最后一次出栈的栈顶元素其对应的值; } } 输出结果;
应用:
以上就是一个单调栈的定义及其实现,下面就来说一下它可以解决哪些问题。其实我也不能给出证明,以证明它为什么能完成这些功能,只是简单的把它的用途说一下,碰到问题时就需要大家灵活运用了。
1.最基础的应用就是给定一组数,针对每个数,寻找它和它右边第一个比它大的数之间有多少个数。
2.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大。
3.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大。
实战练习:
一、应用一:poj3250 单调递减栈
https://www.cnblogs.com/-citywall123/p/10769634.html
二、应用二:poj 2559 单调递增栈
https://www.cnblogs.com/-citywall123/p/10770468.html