数据结构——栈(Stack)详解
1. 栈(Stack)
1.1 概念
栈:一种特殊的线性表,只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中数据元素遵循后进先出LIFO(Last In First Out)的原则
压栈:栈的插入操作可以叫做进栈、压栈、入栈,入数据在栈顶
出栈:栈的删除操作叫做出栈,出数据在栈顶
1.2 栈的使用
| 方法 | 功能 |
| Stack() | 构造一个空的栈 |
| E push(E e) | 将e入栈并返回e |
| E pop() | 将栈顶元素出栈并返回 |
| E peek() | 获取栈顶元素 |
| int size() | 获取栈中有效元素个数 |
| boolean empty() | 检测栈是否为空 |
public static void main(String[] args) {Stack<Integer> s = new Stack<>();s.push(1);s.push(2);s.push(3);s.push(4);System.out.println(s.size());//获取栈中有效数据System.out.println(s.peek());//查看栈顶元素System.out.println(s.pop());//使栈顶元素出栈System.out.println(s.empty());//检测栈是否为空System.out.println(s.isEmpty());//检测栈是否为空,继承自Vector}
System.out.println(s.isEmpty());
上面的isEmpty()方法,查看源码,虽然栈中没有,但是栈继承自Vector,在父类Vector中,有isEmpty方法
1.3 栈的模拟实现
import java.util.Arrays;public class MyStack {public int[] elem;public int usedSize;public MyStack() {this.elem = new int[10];}public void push(int val) {if(isFull()) {//扩容elem = Arrays.copyOf(elem,2*elem.length);}elem[usedSize] = val;usedSize++;}public boolean isFull(){return usedSize == elem.length;}public int pop() {if(empty()) {return -1;}int oldVal = elem[usedSize-1];usedSize--;return oldVal;}public int peek() {if(empty()) {return -1;}return elem[usedSize-1];}public boolean empty() {return usedSize == 0;}
}
使用泛型实现
import java.util.Arrays;public class MyStack<E> {public Object[] elem;public int usedSize;public MyStack() {this.elem = new Object[10];}public void push(E val) {if(isFull()) {//扩容elem = Arrays.copyOf(elem,2*elem.length);}elem[usedSize] = val;usedSize++;}public boolean isFull(){return usedSize == elem.length;}public E pop() {if(empty()) {return null;}E oldVal = (E)elem[usedSize-1];usedSize--;return oldVal;}public E peek() {if(empty()) {return null;}return (E)elem[usedSize-1];}public boolean empty() {return usedSize == 0;}
}
1.4 栈的应用场景
1.将递归转化为循环
//递归方式public void printList(Node head) {if(null != head) {printList(head.next);System.out.println(head.val + " ");}}//运用栈的循环方式public void printList(Node head) {if(null == head) {return;}Stack<Node> s = new Stack<>();//将链表中的节点保存在栈中Node cur = head;while(null != cur) {s.push(cur);cur = cur.next;}//将栈中元素出栈while(!s.empty()) {System.out.println(s.pop().val + " ");}}
2. 括号匹配

class Solution {public boolean isValid(String s) {Stack<Character> stack = new Stack<>();//创建字符类型栈for(int i = 0; i < s.length(); i++) {//遍历字符串,将字符串中字符取出char ch = s.charAt(i);//1.遇到左括号,入栈if(ch == '(' || ch == '[' || ch == '{') {stack.push(ch);}else {//2.遇到右括号//先判断栈是否为空if(stack.empty()) {return false;}else {//3.取栈顶左括号看与当前右括号是否匹配char chL = stack.peek();if(chL == '(' && ch == ')' || chL == '[' && ch == ']' || chL == '{' && ch == '}') {stack.pop();//若左右括号匹配,则栈顶元素出栈}else {return false;}}}}return stack.empty();//最后若栈为空,返回true,栈不为空,返回false}
}
3. 逆波兰表达式求值
解析:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
- 平常使用的算式则是一种中缀表达式,如
( 1 + 2 ) * ( 3 + 4 )。 - 该算式的逆波兰表达式写法为
( ( 1 2 + ) ( 3 4 + ) * )。
逆波兰表达式主要有以下两个优点:
- 去掉括号后表达式无歧义,上式即便写成
1 2 + 3 4 + *也可以依据次序计算出正确结果。 - 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
将中缀表达式转换为后缀表达式的方法:

当逆波兰式运用到栈中,按照次序将数字入栈,若遇到运算符,则取出栈顶两个数字,先取出为运算符右边操作数,后取出为运算符左边操作数(这是为了避免当运算符为 - 或 / 时,顺序不同造成结果不同的问题),如下图:

代码:
public int evalRPN(String[] tokens) {Stack<Integer> s = new Stack<>();for (int i = 0; i < tokens.length; i++) {String tmp = tokens[i];if(!isOpearation(tmp)) {//判断当前字符串是否为运算符Integer val = Integer.valueOf(tmp);//将字符串转换为数字s.push(val);}else {Integer val2 = s.pop();Integer val1 = s.pop();switch(tmp) {case "+":s.push(val1+val2);break;case "-":s.push(val1-val2);break;case "*":s.push(val1*val2);break;case "/":s.push(val1/val2);break;}}}return s.pop();}public boolean isOpearation(String s) {if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) {return true;}return false;}
4. 出栈入栈次序匹配

public boolean IsPopOrder (int[] pushV, int[] popV) {// write code hereStack<Integer> s = new Stack<>();int count = 0;for(int i = 0; i < pushV.length; i++) {s.push(pushV[i]);//这个单独的循环,必须保证栈不为空,count不能越界,栈顶元素等于count下标处值while(!s.empty() && count != popV.length && s.peek() == popV[count]){s.pop();count++;}}return s.empty();}
5. 最小栈

class MinStack {Stack<Integer> s;//普通栈,泛型类型别忘记加!!!Stack<Integer> ms;//最小栈public MinStack() {//构造方法s = new Stack<>();ms = new Stack<>();}public void push(int val) {s.push(val);if(ms.empty()) {//若为第一次入栈操作,则最小栈无条件入栈ms.push(val);}else {Integer peekVal = ms.peek();//若不是第一次,则需与最小栈栈顶元素进行比较if(val <= peekVal) {ms.push(val);}}}public void pop() {if(s.empty()) {return;}int popVal = s.pop();//包装类属于引用类型,不能直接==,所以此处用int接收,自动拆箱if(popVal == ms.peek()) {ms.pop();}}public int top() {if(s.empty()) {return -1;}return s.peek();}public int getMin() {if(ms.empty()) {return -1;}return ms.peek();}
}
1.5 栈、虚拟机栈、栈帧的区别
栈(Stack):是一种只允许在一端进行插入或删除的线性表,满足后进先出的特点
虚拟机栈:逻辑结构,是具有特殊作用的一块内存空间,主管Java程序的运行,它保存方法的局部变量(8种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回
栈帧:函数从调用过程到结束的体现,一个函数从调用到销毁中占用的空间,内部的局部变量统一放在栈帧中。每个函数在运行时,JVM都会创建一个栈帧,然后将栈帧压入到虚拟机栈中,当函数调用结束时,该函数对应的栈帧会从虚拟机栈中出栈
相关文章:
数据结构——栈(Stack)详解
1. 栈(Stack) 1.1 概念 栈:一种特殊的线性表,只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中数据元素遵循后进先出LIFO(Last In First Out)的原则 压栈&am…...
1.Element的table表高度自适应vue3+js写法
解决方法 在页面table上添加id,动态计算每页table的最大高度 ,将高度保存在store中,每次切换路由时进行计算。 文章目录 解决方法前言一、页面table使用二、store状态库1.引入库 效果 前言 提示:状态管理使用的是pinia,用法参考…...
联想电脑电池只能充到80%,就不在充电了,猛一看以为坏了,只是设置了养护模式。
现在电池管理模式有三种: 1)常规 2)养护 3)快充 好久没有用联想的电脑了,猛一看,咱充到了80%不充了,难道电池是坏的?我们要如何设置才可以让其充电到100%呢? 右下角…...
Unity接入PS5手柄和Xbox手柄以及Android平台的(以及不同平台分析)
Unity接入PS5手柄和Xbox手柄以及Android平台的(以及不同平台分析) 介绍Unity手柄小知识PC端和编辑器上的摇杆事件和滑动事件PS5手柄Xbox手柄北通手柄 安卓环境下(安卓手机或者安卓模拟器)PS5手柄Xbox手柄北通手柄 总结 介绍 最近…...
vue+java实现简易AI问答组件(基于百度文心大模型)
一、需求 公司想要在页面中加入AI智能对话功能,故查找免费gpt接口,最终决定百度千帆大模型(进入官网、官方文档中心); 二、主要功能列举 AI智能对话;记录上下文回答环境;折叠/展开窗口&#…...
刷代码随想有感(104):动态规划——01背包问题/二维dp数组
题干: 代码: #include<bits/stdc.h> using namespace std; int n,bagweight; void solve(){vector<int>weight(n, 0);vector<int>value(n, 0);for(int i 0; i < n; i){cin>>weight[i];}for(int j 0; j < n; j){cin>…...
Docker-Portainer可视化管理工具
Docker-Portainer可视化管理工具 文章目录 Docker-Portainer可视化管理工具介绍资源列表基础环境一、安装Docker二、配置Docker加速器三、拉取Portainer汉化版本镜像四、运行容器五、访问可视化界面 介绍 Portainer是一款开源的容器管理平台,它提供了一个直观易用的…...
SqlSugar 集成
1 关于 SqlSugar SqlSugar 是 .NET/C# 平台非常优秀的 ORM 框架,目前 Nuget 总下载突破 700K,Github 关注量也高达 3.2K,是目前当之无愧的国产优秀 ORM 框架之一。 SqlSugar 官方地址:果糖网 ( SqlSugar 官网 &#…...
MySQL Connector/C++ 和 MySQL Connector/ODBC 的区别
MySQL Connector/C++ 和 MySQL Connector/ODBC 是两种不同的数据库连接工具,它们各自有不同的特点和用途。以下是它们之间的一些主要区别: 1. **编程接口**: - MySQL Connector/C++ 提供了面向对象的编程接口,它是用C++编写的,提供了C++特有的类和对象来与MySQL数据库…...
Weevil-Optimizer象鼻虫优化算法的matlab仿真实现
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 Weevil-Optimizer象鼻虫优化算法的matlab仿真实现,仿真输出算法的优化收敛曲线,对比不同的适应度函数。 2.测试软件版本以及运行结果展示…...
Web前端项目-交互式3D魔方【附源码】
交互式3D魔方 3D魔方游戏是一款基于网页技术的三维魔方游戏。它利用HTML、CSS和JavaScript前端技术来实现3D效果,并在网页上呈现出逼真的魔方操作体验。 运行效果: 一:index.html <!DOCTYPE html> <html><head><…...
视频格式转换avi格式怎么弄?分享视频转换方法
视频格式转换avi格式怎么弄?AVI作为一种广泛支持的视频格式,能够在多种设备和播放器上顺畅播放,确保我们的视频内容能够无障碍地分享给朋友或上传至各大平台。其次,AVI格式通常具有较好的兼容性,能够避免格式转换过程中…...
UniRx 入门
Reactive X 是 Reactive Extensions 的缩写,一般简写为 Rx,最初是 LINQ 的一个扩展,由微软的团队开发,Rx 是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流,支持大部…...
简单游戏制作——飞行棋
控制台初始化 int w 50; int h 50; ConsoleInit(w, h); static void ConsoleInit(int w, int h) {//基础设置//光标的隐藏Console.CursorVisible false;//舞台的大小Console.SetWindowSize(w, h);Console.SetBufferSize(w, h); } 场景选择相关 #region 场景选择相关 //声…...
等保一体机
等保一体机是面向等保场景推出的合规型安全防护产品。基于“一个中心,三重防护”的设计理念,通过内置全面、多样的安全能力,为政府、医疗、教育、企业等中小型客户提供快速合规、按需赋能的一站式等保合规解决方案。 等保一体机要求管理网络和…...
什么是寄存器文件(Register File)?
寄存器文件(Register File)是计算机系统中用于存储处理器操作数的小型、快速的存储单元集。它在 CPU 内部,提供极高的访问速度,通常用于存储临时数据、操作数和指令执行过程中的中间结果。 寄存器文件的组成和特点 寄存器集&…...
6月15号作业
使用手动连接,将登录框中的取消按钮使用第二中连接方式,右击转到槽,在该槽函数中,调用关闭函数 将登录按钮使用qt4版本的连接到自定义的槽函数中,在槽函数中判断ui界面上输入的账号是否为"admin"࿰…...
零基础入门学用Arduino 第三部分(三)
重要的内容写在前面: 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后,整体感觉是很好的,如果有条件的可以先学习一些相关课程,学起来会更加轻松,相关课程有数字电路…...
Trusty qemu + android环境搭建详细步骤
下载源码 mkdir trusty cd trusty repo init -u https://android.googlesource.com/trusty/manifest -b master repo sync -j32 编译 ./trusty/vendor/google/aosp/scripts/build.py generic-arm64 查看编译结果 ls build-root/build-generic-arm64/lk.bin 安装运行依赖 …...
杀戮尖塔游戏
Java 你正在玩策略卡牌杀戮尖塔游戏,轮到你出牌,手里N张攻击卡,每张都需要花金币coust[i]和获得伤害dmager[i]。 最多花3金币能造成的最大伤害是多少? class Solution{public int calc(int[] cost, int[] dmager, N){int[][] db …...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...
[USACO23FEB] Bakery S
题目描述 Bessie 开了一家面包店! 在她的面包店里,Bessie 有一个烤箱,可以在 t C t_C tC 的时间内生产一块饼干或在 t M t_M tM 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC,tM≤109)。由于空间…...
