首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >单调栈

单调栈

作者头像
Steve Wang
发布于 2020-12-16 18:00:54
发布于 2020-12-16 18:00:54
1K0
举报
文章被收录于专栏:从流域到海域从流域到海域

栈(Stack)是一种操作受限的线性表,只允许一端进,同一端出,因而具有后进先出(LIFO)的特性。

单调栈(Monotonic Stack)是一种特殊的栈,它首先是一个栈,其次栈中的所有元素单调递增或者单调递减。

单调栈在算法中的应用在于它能够在一次扫描即O(n)的复杂度之内找到数组中每一个元素的前上界(单增栈)或者前下界(单减栈)。

代码语言:javascript
复制
// 递增栈
for(int i=0; i<arr.length; i++) {
	while(!stack.empty() && stack.peek() >= arr[i]) {
		stack.pop();
	}
	stack.push(arr[i]);
}

// 递减栈
for(int i=0; i<arr.length; i++) {
	while(!stack.empty() && stack.peek() <= arr[i]) {
		stack.pop();
	}
	stack.push(arr[i]);
}

在上面的单调栈中,访问到任一个数组元素时,当前栈顶就是该元素的前上界(递增栈)或者前下界(递减栈)。

下面以实际算法题来说明递增栈的作用。

[编程题]逛街

链接:https://www.nowcoder.com/question/next?pid=21283868&qid=830860&tid=37511217 来源:牛客网

时间限制:C/C++ 2秒,其他语言4秒空间限制:C/C++ 256M,其他语言512M 小Q在周末的时候和他的小伙伴来到大城市逛街,一条步行街上有很多高楼,共有n座高楼排成一行。 小Q从第一栋一直走到了最后一栋,小Q从来都没有见到这么多的楼,所以他想知道他在每栋楼的位置处能看到多少栋楼呢?(当前面的楼的高度大于等于后面的楼时,后面的楼将被挡住) 输入描述: 输入第一行将包含一个数字n,代表楼的栋数,接下来的一行将包含n个数字wi(1<=i<=n),代表每一栋楼的高度。 1<=n<=100000; 1<=wi<=100000; 输出描述: 输出一行,包含空格分割的n个数字vi,分别代表小Q在第i栋楼时能看到的楼的数量。 示例1 输入 6 5 3 8 3 2 5 输出 3 3 5 4 4 4 说明 当小Q处于位置3时,他可以向前看到位置2,1处的楼,向后看到位置4,6处的楼,加上第3栋楼,共可看到5栋楼。当小Q处于位置4时,他可以向前看到位置3处的楼,向后看到位置5,6处的楼,加上第4栋楼,共可看到4栋楼。

代码语言:javascript
复制
// 递减栈 栈中元素数目即为能看到的楼数目
// 两个栈 一个表示向左能看到的数目 一个表示向右 注意所有栈都不考虑当前所在楼本身 因此最后结果要加1
// 以向右看为例
// 单调栈里维护了从最左边到该位置前递减序列 而到达当前位置的递减序列对于当前位置来 都是可见的
// 因此单调栈的大小保存了能看到楼的个数

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;

public class Tencent2020backend2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] height = new int[n];
        for(int i=0; i<n; i++) {
            height[i] = sc.nextInt();
        }

        Stack<Integer> stack1 = new Stack<>();
        Stack<Integer> stack2 = new Stack<>();
        ArrayList<Integer> count1 = new ArrayList<>();
        ArrayList<Integer> count2 = new ArrayList<>();

        for(int i = 0, j = height.length-1; i < height.length && j>=0; i++, j--) {
            count1.add(stack1.size());
            count2.add(0, stack2.size());
            while(!stack1.empty() && stack1.peek() <= height[i]) stack1.pop();
            while(!stack2.empty() && stack2.peek() <= height[j]) stack2.pop();
            stack1.push(height[i]);
            stack2.push(height[j]);
        }
        for(int i=0; i<n; i++) {
            System.out.print(count1.get(i) + count2.get(i) + 1 + " ");
        }
    }
}

LeetCode 84. Largest Rectangle in Histogram (hard)

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。

图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。 示例: 输入: [2,1,5,6,2,3] 输出: 10

我下面给出的解法比官方题解还要简洁,注意栈中存的是索引而不是元素,因为要作为宽度计算面积:

代码语言:javascript
复制
# 递增栈
# 每次遇到非递增元素 可以计算一次面积 在所有计算的面积中找到最大的即可
# 巧妙的利用了递增栈的性质

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        stack = [-1]
        heights.append(-1)
        res = 0

        for idx, val in enumerate(heights):
            while heights[stack[-1]] > val:
                h = heights[stack.pop()]
                res = max(res, h*(idx - stack[-1]-1)) # idx-stack[-1]-1是当前递增序列的宽度
            stack.append(idx)
        return res
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/12/12 ,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • [编程题]逛街
  • LeetCode 84. Largest Rectangle in Histogram (hard)
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档