栈和队列 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就行了 …

【Java】二维数组

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

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…

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

模块功能&#xff1a; 我个人目前在做一个电影类的网站&#xff0c;在开发文章页的模版时候&#xff0c;突然觉得给文章页增加一个“谁对本电影感兴趣”的功能模块可能会比较有趣&#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 内有两套不同…

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

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

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

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

FFmpeg开发环境搭建

FFmpeg是音视频开发必备的库&#xff0c;也是唯一的库。本文主要讲解在ubuntu22和macOS14环境下的编译安装。 为什么要自己编译呢&#xff1f;其中一个很重要的原因就是ffmpeg在编译时可以加入很多插件&#xff0c;这种特定的库网络上可能找不到编译好的版本&#xff0c;另外如…

在自定义总线下注册设备

1、自定义总线下注册设备 //my_bus_dev.c #include<linux/module.h> #include<linux/init.h> #include<linux/kernel.h> #include<linux/kobject.h> #include<linux/slab.h> #include<linux/sysfs.h> #include<linux/device.h> #in…

solidity实战练习3——荷兰拍卖

//SPDX-License-Identifier:MIT pragma solidity ^0.8.24; interface IERC721{function transFrom(address _from,address _to,uint nftid) external ; }contract DutchAuction { address payable immutable seller;//卖方uint immutable startTime;//拍卖开始时间uint immut…

属于马云的时代结束了

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 马云突然回国了&#xff0c;还出现在阿里巴巴的大厦里。大家都非常激动&#xff0c;阿里沸腾了&#xff0c;整个杭州&#xff0c;甚至全网都沸腾了&#xff0c;日本慌了&#xff0c;美国坐不住了&#xff0c;欧洲陷…

CSS技巧专栏:一日一例 5-纯CSS实现背景色从四周向中心填充的按钮特效

特此说明 本专题专注于讲解如何使用CSS制作按钮特效。前置的准备工作和按钮的基本样式,都在本专栏第一篇文章中又详细说明。自本专栏第四篇文章起,本专栏都将直接跳过前面的内容,不再重复复制,需要了解按钮基础样式的同学,请移步:《CSS技巧 - 一日一例 (1):会讨好的热…

关键路径-matlab

路径上边的数目称为路径长度 图的基本知识 求最短路径&#xff08;Dijkstra算法&#xff09; 2. 待继续尝试 ①Dijkstra ②floyd_all.m 一 二 ③ LeetCode [329. 矩阵中的最长递增路径]

SpringCloud---zuul路由网关

zuul网关 zuul网关定义 Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet(filter)应用。Zuul 在云平台上提供动态路由&#xff0c;监控&#xff0c;弹性&#xff0c;安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的…

Kithara与OpenCV (二)

Kithara使用OpenCV QT 进行特征检测 目录 Kithara使用OpenCV QT 进行特征检测OpenCV 特征检测简介Qt应用框架简介项目说明关键代码抖动测试测试平台&#xff1a;测试结果&#xff1a;结论 OpenCV 特征检测简介 OpenCV是一个开源的计算机视觉库&#xff0c;提供了各种图像处理…

一图展示免费开源的分布式版本控制系统​Git

文章目录 前言一、安装Git二、Git配置三、git命令 前言 Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。也是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。 一、安装Git Windows操作系统…

如何更好的优化 ListView 控件的性能

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD&#xff0c;日常还会涉及Android开发工作。 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起…

MongoDB教程(四):mongoDB索引

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MongoD…

windows查看局域网所有设备ip

windows如何查看局域网所有设备ip 操作方法 一 . 在搜索栏里输入cmd 二 .在命令行黑窗口输入arp -a 三 . 最上面显示的动态地址就是所有设备ip