代码随想录:单调栈4-5

42.接雨水

题目

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

代码(单调栈)

class Solution {
    public int trap(int[] height) {

        //雨水的关键是找凹槽,凹槽是栈顶的mid,右边柱子是当前更大的元素,左边的柱子是栈顶下面的元素
        //单调递减栈:寻找右边第一个更大的元素
        Stack<Integer> stack = new Stack<>();
        //栈中放的是元素下标,第一个元素入栈,高度=height[0]
        stack.push(0);

        int sum = 0;  //初始雨水

        //i是当前元素下标,height[i]是当前元素高度
        for(int i=1; i < height.length; i++){
            //当前元素高度 < 栈顶元素高度
            if(height[i] < height[stack.peek()]){
                stack.push(i);  //当前元素下标入栈
            }
            //当前元素高度 = 栈顶元素高度
            else if(height[i] == height[stack.peek()]){
                //因为栈顶高度和当前元素高度,栈顶的柱子肯定没有雨水
                stack.pop();  //栈顶元素入栈
                stack.push(i);  //当前元素下标入栈
            }
            //当前元素高度 > 栈顶元素高度,出现凹槽
            else{
                //只要当前元素高度比栈顶元素高度大,循环处理所有凹槽
                while(!stack.isEmpty() && height[i] > height[stack.peek()]){
                    int mid = stack.pop();  //凹槽下标

                    //mid出栈后,如果栈为空,说明mid左边是空的,没有雨水,[1],mid=1,1出栈就行
                    //mid出栈后,如果栈非空,说明mid左边不空,有雨水,需要计算雨水量
                    if(!stack.isEmpty()){
                        int left = stack.peek();  //凹槽左边下标
                        //mid凹槽的雨水高度 = 左右高度最小值-凹槽高度
                        int h = Math.min(height[i], height[left]) - height[mid];
                        //mid凹槽的雨水宽度
                        int w = i - left - 1;  //这里要写- left - 1,不能写-mid,[4,2,0,3,2,5]画个图就知道了
                        int area = h * w;  //mid凹槽雨水量
                        sum += area;   
                    }
                }
                stack.push(i);//当前元素下标入栈
            }
        }
        return sum;  //返回总雨水量
    }
}

代码(双指针)

class Solution {
    public int trap(int[] height) {
        int[] left = new int[height.length];
        int[] right = new int[height.length];

        left[0] = height[0];
        for(int i=1; i < height.length; i++){
            left[i] = Math.max(height[i], left[i-1]);
        }

        right[height.length-1] = height[height.length -1];
        for(int i=height.length-2; i >= 0; i--){
            right[i] = Math.max(height[i], right[i+1]);
        }

        int res = 0;
        for(int i=0; i < height.length; i++){
            int area = Math.min(left[i], right[i]) - height[i];
            res += area;
        }
        return res;
    }
}

84.柱状图中最大的矩形

题目

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

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

示例 1:

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

输入: heights = [2,4]
输出: 4

代码

class Solution {
    public int largestRectangleArea(int[] heights) {
        //关键是找变小的元素,变小了,就把前面的矩形面积计算出来

        //扩容,头尾加元素0
        int[] newHeights = new int[heights.length + 2];
        //头部加0,防止出现[8,6,4,2]递减序列算出来是0
        newHeights[0] = 0;
        //尾部加0,防止出现[2,4,6,8]递增序列算出来是0
        newHeights[newHeights.length - 1] = 0;
        //数组copy
        for(int i=0; i < heights.length; i++){
            newHeights[i+1] = heights[i];
        }

        int res = 0; //初始最大面积
        //单调栈,找右边第一个更小的元素
        Stack<Integer> stack = new Stack<>();
        //第一个元素下标入栈
        stack.push(0);  

        for(int i=1; i < newHeights.length; i++){
            //当前元素高度 > 栈顶元素高度
            //举例:[1,5,6]持续入栈
            if(newHeights[i] > newHeights[stack.peek()]){
                stack.push(i);  //下标入栈
            }
            //当前元素高度 = 栈顶元素高度
            //举例:[1,5,5,6],当前元素=2,最大值会在第一个5取到,5不用出栈
            else if(newHeights[i] == newHeights[stack.peek()]){
                stack.push(i);  //下标入栈
            }
            //当前元素高度 < 栈顶元素高度
            else{
                //[1,5,6],当前元素=2,要出栈循环计算矩形大小
                while(!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]){
                    int mid = stack.pop();  //6的下标
                    if(!stack.isEmpty()){
                        int left = stack.peek();  //5的下标
                        int h = newHeights[mid]; //高度是6
                        int w = i - left - 1;  //宽度是1
                        int area = h * w; //面积是6
                        res = Math.max(res, area);  //更新最大矩形面积
                    }
                }
                stack.push(i);
            }
        }
        return res;
    }
}
### Java 中栈和队列的实现及相关练习 #### 栈 (Stack) 的基本概念与实现 栈是一种遵循 **后进先出(LIFO, Last In First Out)** 原则的数据结构。在 Java 中,可以使用 `Deque` 接口或者数组来模拟栈的行为。 以下是通过 `Deque` 实现的一个简单栈的例子: ```java import java.util.ArrayDeque; import java.util.Deque; public class StackExample { private Deque<Integer> stack; public StackExample() { this.stack = new ArrayDeque<>(); } public void push(int value) { stack.addFirst(value); } public int pop() { return stack.removeFirst(); } public boolean isEmpty() { return stack.isEmpty(); } } ``` 此代码展示了如何利用 `Deque` 来创建一个简单的栈[^1]。 --- #### 队列 (Queue) 的基本概念与实现 队列是一种遵循 **先进先出(FIFO, First In First Out)** 原则的数据结构。Java 提供了多种内置类用于操作队列,比如 `LinkedList` 和 `PriorityQueue`。 下面是一个基于 `LinkedList` 的队列实现示例: ```java import java.util.LinkedList; import java.util.Queue; public class QueueExample { private Queue<Integer> queue; public QueueExample() { this.queue = new LinkedList<>(); } public void enqueue(int value) { queue.offer(value); } public int dequeue() { return queue.poll(); } public boolean isEmpty() { return queue.isEmpty(); } } ``` 上述代码片段说明了如何使用 `LinkedList` 构建 FIFO 行为的队列。 --- #### 使用栈和队列解决实际问题 一些经典的算法题目可以通过栈和队列得到优雅的解决方案: 1. **用栈实现队列** 可以借助两个栈完成队列的功能。入队时将数据压入第一个栈;出队时如果第二个栈为空,则将第一个栈的所有元素依次弹出并压入第二个栈,最后从第二个栈弹出顶部元素即可[^5]。 2. **滑动窗口最大值** 这一问题是典型的单调队列应用场景之一。维护一个双端队列,在遍历过程中动态调整队首和队尾的位置,从而快速获取当前窗口的最大值[^3]。 3. **有效括号匹配** 利用栈判断给定字符串中的括号是否合法。每当遇到左括号将其推入栈中,而碰到右括号时尝试从栈顶取出配对的左括号进行验证[^2]。 4. **逆波兰表达式求值** 同样依赖于栈的操作逻辑解析 RPN(Reverse Polish Notation)。对于每一个运算符,都需要从栈里提取最近两次存储的结果执行相应计算后再存回栈内等待后续处理。 5. **前 K 个高频元素** 结合哈希表统计频率以及优先级队列筛选最高频次项共同作用下达成目标效果。 --- #### 关于刷题技巧 针对 LeetCode 上涉及栈和队列类型的习题,推荐按照如下策略开展学习活动: - 熟悉各种 ADT(抽象数据类型),包括但不限于标准库里的容器组件及其特性差异; - 多加实践动手编写程序解决问题的能力训练过程之中不断积累经验教训总结规律形成自己的套路模式; - 定期回顾错题集加深印象巩固薄弱环节直至完全掌握为止[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

守岁白驹hh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值