栈和队列 OJ (一)

括号匹配问题

题目链接:
https://leetcode.cn/problems/valid-parentheses/

在这里插入图片描述


遇到左括号入栈,遇到右括号,我们就出栈看看括号是否匹配

这里要注意如果左括号多于右括号的情况下,字符串循环遍历结束时,栈不为空,所以循环结束后,最后我们要把栈是否为空保判断一下。

如果是右括号多余左括号的情况下,栈走到空的时候,字符串一定没有遍历完,所以在循环中判断括号是否匹配的时候,我们要注意栈是否为空这一个条件。

class Solution {public boolean isValid(String s) {Stack<Character> stack = new Stack<>();int len = s.length();for(int i=0;i<len;i++) {char str = s.charAt(i);if(str == '(' || str == '[' || str == '{') {//入栈stack.push(str);} else {if(stack.empty()) {return false;} else if(!(isEquals(stack.pop(),str))) {return false;}}}if(stack.empty()) {return true;}return false;}private boolean isEquals(char ch1, char ch2) {if((ch1 == '(' && ch2 == ')') || (ch1 == '[' && ch2 == ']') || (ch1 == '{' && ch2 == '}')) {return true;}return false;}
}

栈的压入、弹出序列

题目链接:
https://www.nowcoder.com/share/jump/9257752291720681998243

在这里插入图片描述


我们采用双指针的方法,i 遍历 入栈序列,j 遍历 出栈序列,当 i 和 j 所表示的元素相同时,进入出栈循环中,出栈要注意栈不能为空并且栈顶元素和 j 代表的元素要相同才能继续循环下去,否则 i 下标继续移动,直到 出栈序列完全遍历完。

在遍历入栈序列的时候,如果 i 和 j 所表示的元素不相同的话, i 所表示的元素直接入栈。

最后入栈序列遍历完成后,就可以判断 j 是否也遍历完出栈序列,没有则说明出栈序列不符合条件直接返回false,否则返回 true

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param pushV int整型一维数组 * @param popV int整型一维数组 * @return bool布尔型*/public boolean IsPopOrder (int[] pushV, int[] popV) {// write code hereStack<Integer> stack = new Stack<>();int len1 = pushV.length;int len2 = popV.length;int j = 0;int i = 0;for(i=0;i<len1;i++) {if(pushV[i] != popV[j]) {stack.push(pushV[i]);} else {j++;while(!stack.empty() && stack.peek() == popV[j]) {j++;stack.pop();}}}if(j < len2) {return false;}return true;}
}

逆波兰表达式

题目链接:
https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/

在这里插入图片描述


首先介绍前缀,中缀,后缀表达式:
前缀表达式是指运算符在两个操作数前面,例如 1 + 1 转化为前缀表达式就变成 + 1 1,前缀表达式也叫波兰表达式
中缀表达式是指运算符在两个操作数中间,也就是我们经常使用的表达式的顺序,例如 1 + 1
后缀表达式是指运算符在两个操作数后面,例如 2 + 1 表示为 2 1 + ,后缀表达式也叫逆波兰表达式

后缀表达式怎么转成中缀表达式?
从第一个元素开始找,直到遇到运算符,就从前面拿出两个操作数,分别为 第一个操作数和第二个操作数,将运算符放到它们中间,整合成新的式子作为操作数放回,以此类推,直到所有运算符找完为止,最后就会得到中缀表达式。

举个例子:
8 1 2 + 6 - *
首先遇到加号 ,拿出前两个操作数 ,将加号放在中间,即为 1 + 2 ,由于还没找完所有的运算符将式子放回去,即 8 (1 + 2 )6 - *
接着遇到减号,还是一样,拿出前两个操作数,将减号放入二者之间,即 (1 + 2 ) - 6 ,由于还没找完所有的运算符将式子放回去,即 8 ( (1 + 2 ) - 6 )*
最后遇到乘号,拿出前两个操作数,将乘号放入二者之间,即( 8 * ( (1 + 2 ) - 6 )),所有运算符找完,最后的式子就是中缀表达式。


回到代码题上,题目要求我们将逆波兰表达式的结果求出,那么我们就要将逆波兰表达式转化为中缀表达式,这时候可以使用栈这个数据结构,遍历整个数组,遇到运算符,出栈两次,第一次出栈是第二个操作数,第二次出栈是第一个操作数,将结果运算出来然后入栈。
如果没有遇到运算符的话,就要入栈,直到遇到运算符。
最后栈只会剩下一个元素,这个元素就是逆波兰表达式。

这里有一个方法要注意:字符串转整型可以使用包装类的Integer 里面的静态方法 parseInt(String s)

class Solution {public int evalRPN(String[] tokens) {int len = tokens.length;Stack<Integer> stack = new Stack<>();int val1 = 0;int val2 = 0;for(int i=0;i<len;i++) {String ch = tokens[i];switch(ch) {case "+" :val2 = stack.pop();val1 = stack.pop();stack.push(val1 + val2);break;case "-":val2 = stack.pop();val1 = stack.pop();stack.push(val1 - val2);break;case "*":val2 = stack.pop();val1 = stack.pop();stack.push(val1 * val2);break;case "/":val2 = stack.pop();val1 = stack.pop();stack.push(val1 / val2);break;default: stack.push(Integer.parseInt(ch));break;}}return stack.pop();}
}

拓展

如何快速将中缀表达式转化为后缀表达式?
根据运算优先级先后给每两个操作数加括号
在每一个括号后面把里面的运算符给拉出来,之后删掉所有的括号即可

举个例子:
将中缀表达式 ( a + b ) * c - ( a + b ) / e 转化为后缀表达式

第一步:加括号
( ( ( a + b ) * c ) - ( ( a + b ) / e ) )

第二步: 拉出运算符
( ( ( a b ) + c ) * ( ( a b ) + e )/ )-

第三步:去括号
a b + c * a b + e / - 这就是后缀表达式

最小栈

题目链接:
https://leetcode.cn/problems/min-stack/description/

在这里插入图片描述


题目要求我们即有普通栈的功能还能拥有找到最小数值的功能,并且在每次pop之后还是能在O(1)的条件下找到最新的最小值。这时候就需要用到辅助栈了,一个栈是正常使用,另一个栈专门存放最小值。

那第二个栈该怎么使用,因为题目要求我们在栈每次pop之后都能获取到剩下的元素的最小值,所以我们可以每次压栈的时候,就比较这个数据压栈后最新的最小值是多少,然后把这个最小值入栈即可,这样两个栈的元素总数是相同的,这样两个栈就可以同时进行push和pop操作了。

class MinStack {public Stack<Integer> stack;public Stack<Integer> minStack;public MinStack() {stack = new Stack<>();minStack = new Stack<>();}public void push(int val) {stack.push(val);if(minStack.empty()) {minStack.push(val);} else {int min = minStack.peek();if(min > val) {min = val;}minStack.push(min);}}public void pop() {minStack.pop();stack.pop();}public int top() {return stack.peek();}public int getMin() {return minStack.peek();}
}

设计循环队列

题目链接:
https://leetcode.cn/problems/design-circular-queue/description/

在这里插入图片描述


这里我使用顺序队列,因为顺序队列比链式队列好写

循环队列,我们可以使用牺牲一个空间的代价来实现循环:

在这里插入图片描述

当 rear == front 时队列为空,入队时使用 rear = ( rear + 1 ) % 数组长度 实现 rear 的移动,出队使用 front = ( front + 1 ) % 数组长度 实现 front 的移动,如果对这些式子不熟悉的,可以看一下我的博客文章JavaDS —— 栈 Stack 和 队列 Queue 里面有对循环队列的讲解

这里要格外的注意判断队列是否已满的方法,因为直接使用 rear + 1 == front 可能会发生一个情况,就是 rear 下一个数组就越界了,需要对准到正确的下标,即rear 此时为 数组长度-1 ,经过 +1 后就不会变成 0 而是变成数组的长度发生了越界,所以要使用 rear = ( rear + 1 ) % 数组长度 == front 判断队列是否已满

题目的要求是放入k 个数据,而我们的循环队列会牺牲一个空间,为了让队列能装下 k 个数据,所以我们在构造方法里要初始化 k+1 个空间的数组。

class MyCircularQueue {int front;int rear;int[] elem;public MyCircularQueue(int k) {elem = new int[k+1];}public boolean enQueue(int value) {if(isFull()) {return false;}elem[rear] = value;rear = (rear + 1) % elem.length;return true;}public boolean deQueue() {if(isEmpty()) {return false;}front = (front + 1) % elem.length;return true;}public int Front() {if(isEmpty()) {return -1;}return elem[front];}public int Rear() {if(isEmpty()) {return -1;}return elem[(rear-1+elem.length) % elem.length];}public boolean isEmpty() {if(rear == front) {return true;}return false;}public boolean isFull() {if((rear + 1) % elem.length == front) {return true;}return false;}
}

用队列实现栈

题目链接:
https://leetcode.cn/problems/implement-stack-using-queues/

在这里插入图片描述


我们要使用两个队列来模拟栈,我们先回顾栈和队列:
在这里插入图片描述

选择我们来看一下队列怎么模拟出栈:
在这里插入图片描述
在这里插入图片描述
也就是说我们把一个有数据的队列先将元素出队,移动到另一个空队列上进行保存,然后还剩下最后一个元素直接出队就实现了模拟出队。

知道怎么出栈也就知道怎么模拟获取栈顶元素,这里不赘述。


现在我们来思考入栈怎么实现?

由于前面的出栈操作,我们就知道我们需要把所有的数据放在同一个队列上,保证一个队列是存放所有的数据,另一个队列为空队列,剩下的就交给你了。

什么时候是空栈?
显而易见,当两个队列均为空的时候就是空栈。

class MyStack {Queue<Integer> queue1;Queue<Integer> queue2;public MyStack() {queue1 = new LinkedList<>();queue2 = new LinkedList<>();}public void push(int x) {if(empty()) {queue1.offer(x);} else if(queue2.isEmpty()) {queue1.offer(x);} else {queue2.offer(x);}}private void offerAndPoll(Queue x, Queue y) { //x 有元素,y 为空队列int size = x.size();for(int i=0; i<size-1; i++) {y.offer(x.poll());}}public int pop() {if(!queue1.isEmpty()) {offerAndPoll(queue1,queue2);return queue1.poll();} else {offerAndPoll(queue2,queue1);return queue2.poll();}}public int top() {int ret = 0;if(!queue1.isEmpty()) {offerAndPoll(queue1,queue2);ret = queue1.poll();queue2.offer(ret);} else {offerAndPoll(queue2,queue1);ret = queue2.poll();queue1.offer(ret);}return ret;}public boolean empty() {if(queue1.isEmpty() && queue2.isEmpty()) {return true;}return false;}
}

用栈实现队列

题目链接:
https://leetcode.cn/problems/implement-queue-using-stacks/description/

在这里插入图片描述


我们先看一下如何模拟出队:
在这里插入图片描述

在这里插入图片描述

假设此时进行入队 操作的话,会怎么样?

在这里插入图片描述
这时我们会发现stack2的顺序已经混乱,即使你加一个标记符,表示这时候如果进行出队的话,直接二号栈出栈即可,但是如果一直进行入队,你就很难判断二号栈接下来的模拟出队操作是出栈还是先将上面的元素移动到一号栈留下最后一个元素出栈,所以我们这里采用的是一号栈为主栈,二号栈为辅组栈,就是出队入队之后所有的元素最后都必须在一号栈上,二号栈只是用来辅助一号栈更好地完成出队或者获取队头信息的操作。
在这里插入图片描述

我们可以定义两个方法,一个方法是用于模拟出队或者获取队头元素的,也就是将一号栈留下栈底最后一个元素,其余元素全部移动到二号栈这个辅助栈上,另一个方法是用来善后的,即将二号栈所有的元素移动回一号栈上。

class MyQueue {Stack<Integer> stack1;Stack<Integer> stack2;public MyQueue() {stack1 = new Stack<>();stack2 = new Stack<>();}public void push(int x) {stack1.push(x);}private void move(Stack x, Stack y) { // x 有元素, y 为空栈int size = x.size() - 1;for(int i=0; i<size; i++) {y.push(x.pop());}}private void moveAll(Stack x, Stack y) { // x 有元素, y 为空栈int size = x.size();for(int i=0; i<size; i++) {y.push(x.pop());}}public int pop() {move(stack1,stack2);int ret = stack1.pop();moveAll(stack2,stack1);return ret;}public int peek() {move(stack1,stack2);int ret = stack1.peek();moveAll(stack2,stack1);return ret;}public boolean empty() {return stack1.empty();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/45720.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android --- Kotlin学习之路:自己写一个SDK给别的APP用(暴漏一个接口,提供学生的身高数据)

今天又来肝kotlin了&#xff0c;主题是&#xff1a;用kt写一个SDK给其他人用&#xff0c;这个小技能在项目中会经常用到&#xff0c;应该有很多小伙伴还不会用&#xff0c;不会的请往下看—⬇ 在项目里面新建一个module 选择Android library&#xff0c;然后点击finish就行了 …

React有哪些优点和缺点

React 作为一个广泛使用的 JavaScript 库&#xff0c;在前端开发领域具有显著的优点&#xff0c;但同时也存在一些缺点。以下是对 React 的一些主要优点和缺点的概述&#xff1a; 优点 组件化开发&#xff1a; React 鼓励将 UI 分解为小的、独立的、可复用的组件。这种方式使得…

PostgreSQL UPDATE 命令

PostgreSQL UPDATE 命令 PostgreSQL 是一种功能强大的开源对象关系型数据库管理系统(ORDBMS),它使用并扩展了SQL语言。在处理数据库时,我们经常需要更新现有的记录。在PostgreSQL中,UPDATE命令用于修改表中的现有记录。 基本语法 UPDATE命令的基本语法如下: UPDATE t…

常用的内部排序算法

常用的内部排序算法 简单选择排序、直接插入排序和冒泡排序、折半插入排序、希尔排序算法、快速排序算法&#xff08;递归和非递归&#xff09;、堆排序 运行结果&#xff1a; python 输入数据15,5,6,7,8,9,10 [外链图片转存中…(img-60STknHj-1720750359076)] [外链图片转…

【Java】二维数组

文章目录 一、什么是二维数组二、二维数组的声明形式三、二维数组的创建(1)静态初始化(2)动态初始化 四、二维数组的输入五、二维数组在内存中的存储方式 一、什么是二维数组 一维数组的声明是int[] arr&#xff0c;int[] arr {0,1,2,3,…};通过一维数组的形式和表达式 我们…

使用Nginx实现高效负载均衡

概述 Nginx是一款高性能的HTTP和反向代理服务器&#xff0c;广泛用于Web服务的负载均衡。它能有效分发流量至多个后端服务器&#xff0c;提高网站的可用性和响应速度&#xff0c;同时增强系统的可扩展性和安全性。本文将介绍如何配置Nginx进行负载均衡&#xff0c;并提供具体的…

FPGA设计中的流水线 -分割大的计算电路可以更快的处理数据。

FPGA&#xff08;现场可编程门阵列&#xff09;设计中的流水线优化是一种提高设计性能的技术&#xff0c;它通过将设计分解为多个阶段或步骤来实现。每个阶段可以并行执行&#xff0c;从而提高整体的吞吐量和效率。以下是流水线优化的一些关键概念和作用&#xff1a; 意思&…

window下tqdm进度条

原代码是linux下运行&#xff0c;修改后可在window下运行。 #ifndef TQDM_H #define TQDM_H#include <chrono> #include <ctime> #include <numeric> #include <ios> #include <string> #include <cstdlib> #include <iostream> #i…

CollectionUtils的使用

1、非空判断 判断集合是否为空 List<String>对象list&#xff0c;可以使用CollectionUtils中的isEmpty方法来判断list是否为空。代码如下 List<String> list new ArrayList<>(); boolean isEmpty CollectionUtils.isEmpty(list); System.out.println(is…

numpy实现sigmoid函数

numpy实现sigmoid函数 在Python中&#xff0c;可以使用NumPy库的numpy.exp函数来计算e的指数&#xff0c;然后通过除法将其映射到0和1之间&#xff0c;实现sigmoid函数。以下是实现sigmoid函数的示例代码&#xff1a; import numpy as npdef sigmoid(x):return 1 / (1 np.ex…

WordPress 主题技巧:给文章页增加“谁来过”模块。

模块功能&#xff1a; 我个人目前在做一个电影类的网站&#xff0c;在开发文章页的模版时候&#xff0c;突然觉得给文章页增加一个“谁对本电影感兴趣”的功能模块可能会比较有趣&#xff0c;这个功能有点类似于‘足迹’的感觉&#xff0c;用户可以通过这个功能&#xff0c;发…

Log4j的原理及应用详解(三)

本系列文章简介&#xff1a; 在软件开发的广阔领域中&#xff0c;日志记录是一项至关重要的活动。它不仅帮助开发者追踪程序的执行流程&#xff0c;还在问题排查、性能监控以及用户行为分析等方面发挥着不可替代的作用。随着软件系统的日益复杂&#xff0c;对日志管理的需求也日…

前端的页面代码

根据老师教的前端页面的知识&#xff0c;加上我也是借鉴了老师上课所说的代码&#xff0c;马马虎虎的写出了页面。如下代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</ti…

k8s 部署 metribeat 实现 kibana 可视化 es 多集群监控指标

文章目录 [toc]环境介绍老(来)板(把)真(展)帅(示)helm 包准备配置监控集群获取集群 uuid生成 api_key配置 values.yaml 配置 es 集群获取集群 uuid 和 api_key配置 values.yaml 查看监控 缺少角色的报错 开始之前&#xff0c;需要准备好以下场景 一套 k8s 环境 k8s 内有两套不同…

PostgreSQL 导入 .gz 备份文件

要在PostgreSQL中导入.gz压缩的备份文件&#xff0c;你需要先解压缩该文件&#xff0c;然后使用psql工具导入。以下是步骤和示例代码&#xff1a; 解压缩备份文件&#xff1a; gunzip your_backup_file.gz 确保你有足够的权限来导入数据库。 使用psql导入解压后的文件&…

电脑如何重新分盘——保姆级教程

方法一&#xff1a; 通过此电脑&#xff0c;鼠标右键点击此电脑&#xff0c;点击管理 点击磁盘管理进入 二&#xff0c;磁盘分区 我这里选择的是“磁盘0”的C盘进行操作&#xff0c;一般新电脑拿到手的时候都处于这么一个状态&#xff0c;只有一个磁盘分区。现在我要把C盘拆分…

怎么知道服务器100M带宽可以支持多少人访问?

服务器100M带宽能够支持多少人访问取决于多种因素&#xff0c;包括但不限于网页大小、用户行为、数据传输类型以及预期的使用模式。以下是一些评估100M带宽可以支持多少人访问的考虑因素&#xff1a; 1. 网页大小&#xff1a; - 假设平均网页大小为1MB(虽然实际情况可能更小或更…

从汇编层看64位程序运行——参数传递的底层实现

大纲 小于等于6个参数一个参数总结 两个参数总结 三个参数总结 四个参数总结 五个参数总结 六个参数总结 大于6个参数七个参数总结 在32位系统中&#xff0c;参数的传递主要依靠栈来进行。那么64位系统上&#xff0c;是否依旧符合这个规则呢&#xff1f;答案是“不是”。64位系…

Redis6.2.1版本集群新加副本

测试数据 通过redis-benchmark生成测试数据 ./bin/redis-benchmark -h 172.31.4.18 -p 6381 -a Redis_6.2.1_Sc --cluster -t set -d 128 -n 10000000 -r 100000000 -c 200新加节点 172.31.4.18:6381> AUTH Redis_6.2.1_Sc OK172.31.4.18:6381> cluster meet 172.31.4…

Vue 组件之间的通信方式

Vue 组件之间的通信方式有多种&#xff0c;可以根据具体场景和需求选择合适的方式&#xff1a; Props / Events&#xff1a; 父组件通过 props 向子组件传递数据&#xff0c;子组件通过事件&#xff08;$emit&#xff09;向父组件发送消息。 适用于父子组件之间…