单调栈与队列的概念与练习!【建议收藏】

本文探讨了逆波兰表达式的求值问题,介绍了如何通过操作数栈实现算法,并结合了单调栈和队列在最大子矩形面积和窗口最大值问题中的应用。
摘要由CSDN通过智能技术生成

典型的斐波那契数、青蛙跳台阶和汉诺塔问题,看我的前期文章(递归的讨论!)。今天我们重新说一个题,表达式求值问题

题目:逆波兰表达式求值问题

在线OJ链接

image-20210814153029332

题意:会提供一个一个字符串数组,数组里放的是后缀表达式的结果,如上图中的输入,就是一个后缀表达式,要我们计算出这个式子的结果。

先简单说一个中缀表达式如果转换为后缀表达式吧:我们首先有一个栈,叫操作符栈。

1)如果遇到操作数,我们就直接将其输出。

2)如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈中。

3)如果遇到一个右括号,则将栈元素弹出,一直弹到左括号出来为止。注意,左括号只弹出并不输出。

4)如果遇到任何其他的操作符,如“+”, “-”,“(”等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到" ) “的情况下我们才弹出” ( “,其他情况我们都不会弹出” ( "。

5)如果我们读到了输入的末尾,则将栈中所有元素依次弹出。

请添加图片描述

后缀表达式得到后,问题就来到了,如何将后缀表达式计算出结果了。我们先准备一个栈:操作数栈。从左到右遍历后缀表达式:

  1. 遇到操作数,我们直接压入到操作数栈即可;

  2. 遇到操作符,我们就弹出操作数栈里的两个数据进行计算,然后再次压入计算后的结果;

class Solution {

public int evalRPN(String[] tokens) {

Stack numStack = new Stack<>(); //操作数栈

int size = tokens.length;

for (int i = 0; i < size; i++) {

if (isChar(tokens, i)) { //判断是不是操作符

int num2 = numStack.pop();

int num1 = numStack.pop();

numStack.push(calculate(num1, tokens[i], num2)); //计算的数据重新压入栈中

} else {

numStack.push(Integer.parseInt(tokens[i]));

}

}

return numStack.pop(); //最后栈里剩下的一个数据,就是最后的结果

}

private boolean isChar(String[] tokens, int i) {

if (tokens[i].equals(“+”) || tokens[i].equals(“-”) || tokens[i].equals(“*”)

|| tokens[i].equals(“/”)) {

return true;

}

return false;

}

private int calculate(int num1, String option, int num2) {

if (option.equals(“+”)) {

return num1 + num2;

}

if (option.equals(“-”)) {

return num1 - num2;

}

if (option.equals(“*”)) {

return num1 * num2;

}

if (num2 == 0) {

throw new RuntimeException(“num2 is zero.”);

}

return num1 / num2;

}

}

最后:大家可以尝试,假设题目给定的是中缀表达式,如何将它计算出来?其实思想跟我上面讲的一样,只不过需要自己去转换后缀表达式。一个操作数栈,另一个操作符栈,两个栈配合起来,就能解。

三、单调栈运用

================================================================

题目:最大子矩形面积

在线OJ链接

image-20210814170622427

题意:给定一些柱子,计算相邻的柱子之间所构成的最大矩阵面积。例如上图:5、6高度的柱子,可以组合成面积为10的矩形。

分析:这道题我们就要用到单调递减栈。我们从左到右遍历每一根柱子,假设当前遍历到的柱子就是上图的第四根数组(高度为6),此时这根柱子左边离它最近并且还比它小的就是第三根柱子(高度为5),右边离它最近 并且比它小的柱子就是第五根柱子(高度为2)。 本质上,我们就是在寻找每一根柱子,左右两边最近且比它小的柱子,就能计算出这左右两边的柱子中间的矩形面积。如图:

image-20210814173634682

压栈过程:维持栈顶到栈底是降序的。所以上图压入前3个柱子的数据后: 栈顶到栈顶为{6, 5, 1};

当我们遍历到高度为2的柱子时,就打破了降序的规则,所以我们就要弹出栈中的元素,直到压入高度为2的柱子,还能够保持降序即可。

  • 弹出栈顶元素,记为j。新栈顶元素记为k。

因为新栈顶元素(k)肯定是小于刚刚弹出的元素(j)的,所以说:j位置的柱子,左边最多就能够扩展到k+1位置。 又因为当前柱子(i)也是小于刚刚弹出的元素(j)的,所以j位置右边的柱子最多也就扩展到i位置。。所以我们可以得到面积计算公式:(i - k - 1) * height[j]

而在数组遍历完之后,栈中还剩下的数值,它的右边已经不能继续扩展了,所以只能是数组的长度值,所以此时的面积公式是(数组长度 - k - 1) * height[j]

请添加图片描述

class Solution {

public int largestRectangleArea(int[] heights) {

Stack stack = new Stack<>(); //单调递减栈,压入的是下标

int size = heights.length;

int max = -1;

for (int i = 0; i < size; i++) {

while (!stack.isEmpty() && heights[stack.peek()] > heights[i]) { //维持单调递减栈的结构

int j = stack.pop();

int k = stack.isEmpty()? -1 : stack.peek();

int res = (i - k - 1) * heights[j]; // i - (k + 1)

max = Math.max(res, max);

}

stack.push(i);

}

//最后栈中可能还有元素,进行结算

while (!stack.isEmpty()) {

int j = stack.pop();

int k = stack.isEmpty()? -1 : stack.peek();

int res = (size - k - 1) * heights[j];

max = Math.max(max, res);

}

return max;

}

}

三、队列的运用

================================================================

题目:生成窗口最大数值

在线OJ链接

image-20210814195101800

题意:给定一个数组,和一个窗口的大小,从左到右遍历数组,每次移动一个位置,输出窗口中的最大值。

分析:这道题也是一点点“单调”的感觉,也就是单调队列。我们可以用一个双链表或者是数组,用来存储当前元素的下标。队尾到队头,保持升序的状态。

值得注意的是:我们需要注意当前队列的最大值,是否还在窗口的范围内,所以队列里存储不是数据元素,而是对应下标。

请添加图片描述

import java.io.*;

import java.util.*;

public class Main {

public static void main(String[] args) throws IOException {

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

String[] nums = in.readLine().split(" ");

int n = Integer.parseInt(nums[0]);

int w = Integer.parseInt(nums[1]);

nums = in.readLine().split(" ");

LinkedList qmax = new LinkedList<>(); //最大窗口值结构,单调队列结构

for (int i = 0; i < n; i++) {

while (qmax.size() != 0 && Integer.parseInt(nums[qmax.getLast()]) <

Integer.parseInt(nums[i])) {

qmax.removeLast();

}

qmax.addLast(i);

if (qmax.getFirst() <= i - w) { //更新当前最大值,是否还在窗口里

qmax.removeFirst();
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们

目录:

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

Java面试核心知识点

一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

Java面试核心知识点

已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
图片转存中…(img-5DPf2E6l-1713555494415)]

Java面试核心知识点

一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!

[外链图片转存中…(img-b5uqFiIA-1713555494415)]

Java面试核心知识点

已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了

[外链图片转存中…(img-Ml3n7HCp-1713555494416)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值