当前位置: 首页 > news >正文

数据结构-Stack和栈

1.栈

1.1什么是栈

栈是一种特殊的线性表,只允许在固定的一段进行插入和删除操作,进行插入和删除操作的一段称为栈顶,另一端称为栈底。

栈中的数据元素遵顼后进先出LIFO(Last In First Out)的原则,就像一叠盘子,只能在顶部添加或移除盘子。

压栈:栈的插入操作叫做压栈/进栈/入栈,新插入的元素在栈顶。

出战:栈的删除操作叫做出栈,要删除的 元素在栈顶。

从图上观察发现:无论是插入还是删除操作,栈底的位置不发生变化,但是栈顶的位置会随插入或删除操作而发生变化。

1.2栈的常用方法

在Java标准库中提供了java.util.Stack(栈的实现类),此处的常用方法指的是Stack的常用方法。

Stack的常用方法如下表所示:

方法解释
Stack()构造方法,构造一个空的栈
E push(E,e)将元素e入栈,并返回e
E pop()将栈顶元素出栈并返回
E peek()获取栈顶元素
int size()获取栈中有效元素的个数
boolean empty()检验栈是否为空

注意:

pop()是删除元素,并返回删除(删除前的栈顶元素)的元素

peek()是获取栈顶元素并返回,不进行出栈操作

常用方法的代码演示:

public class Test {public static void main(String[] args) {Stack<String> stack=new Stack<>();//入栈stack.push("My");stack.push("name");stack.push("is");stack.push("hajimi");//获取栈中有效元素的个数-->4System.out.println(stack.size());//获取栈顶元素-->hajimi  遵循后进先出System.out.println(stack.peek());//peek方法不会改变栈中的元素个数System.out.println(stack.size());//出栈String deleteElem=stack.pop();//获取栈中有效元素的个数-->3System.out.println(stack.size());//判断栈是否为空-->falseSystem.out.println(stack.isEmpty());}
}

2.Stack

2.1什么是Stack 

Java提供了多种方式来实现栈,包括使用java.util.Stack类,使用Deque接口,或者手动实现栈。

java.util.Stack是Java标准库中提供的一个栈实现类。它继承自Vector类,是一个线程安全的栈实现。


 2.2 Stack的特点

1.线程安全:

由于Stack继承自Vector,它的所有方法都是同步的(线程安全的)。

2.动态扩容:

Stack的底层是Vector,它会根据需要进行动态扩容。

这是Vector的动态扩容方法的原码:

2.3 Stack的局限性

虽然Stack提供了很方便的栈操作,但仍存在一些局限性:

1.继承自Vector:Stack继承自Vector,意味着它继承了一些与栈无关的方法,可能会导致误用

2.性能问题:由于Stack是线程安全的,它的同步机制可能会导致性能的下降,尤其是单线程环境中

2.4实现一个Stack

Stack的底层是Vector,而Vector的底层是数组,接下来我们来编写自己的"Stack"。

代码编写:

package datastructure;import java.util.Arrays;public class MyStack2 {//用来存放栈中的元素private int[] elem;//计算栈中有效元素的个数private int usedSize;//默认初始容量private static final int DEFAULT_SIZE=10;public MyStack2(){this.elem=new int[DEFAULT_SIZE];}//判断栈是否已满private boolean isFull(){return elem.length==usedSize;}//判断栈是否为空private boolean isEmpty(){return usedSize==0;}//元素入栈方法public void push(int val){//判断栈是否已满,如果已满进行扩容if(isFull()){Arrays.copyOf(elem,2*elem.length);}//将元素压入,并将有效元素的个数加一elem[usedSize++]=val;}//元素出栈操作public int pop(){if(isEmpty()){System.out.println("栈中没有元素,无法进行出栈操作");return Integer.MAX_VALUE;}//获取栈顶元素,即要删除的元素int deleteElem=elem[usedSize-1];usedSize--;return deleteElem;}//获取栈顶元素public int peek(){if(isEmpty()){System.out.println("栈为空,无法获取栈顶元素");return Integer.MAX_VALUE;}return elem[usedSize-1];}//计算栈中元素的个数public int size(){return usedSize;}
}

对上述的代码进行运行测试:

public class Test {public static void main(String[] args) {MyStack2 myStack2=new MyStack2();myStack2.push(1);myStack2.push(2);myStack2.push(3);myStack2.push(4);myStack2.push(5);System.out.println("当前栈中元素的个数:"+myStack2.size());System.out.println("获取栈顶元素:"+myStack2.peek());System.out.println("进行出栈:"+myStack2.pop());System.out.println("当前栈中元素的个数:"+myStack2.size());System.out.println("获取栈顶元素:"+myStack2.peek());}
}

运行结果:

3.栈,虚拟机栈,栈帧

概念区分:栈,虚拟机栈和栈帧  

栈:一种数据结构,遵循后进先出(LIFO)的原则,允许在栈顶进行元素的插入和删除操作,常用于括号匹配,表达式求值,深度优先搜索等场景。

虚拟机栈:Java虚拟机(JVM)运行时数据区的一部分,用于存储方法调用,局部变量。每个线程都具有一个自己的虚拟机栈,虚拟机栈中存储的是栈帧

栈帧:是虚拟机栈中的一个条目,用于存储方法调用的相关信息。每个方法调用都会创建一个栈帧,并将其压入虚拟机栈,方法结束后,栈帧会被弹出。

4.栈的应用-逆波兰表达式求值

4.1 什么是逆波兰表达式

逆波兰表达式,也称后缀表达式,是一种特殊的算数表达式表示方式。在逆波兰表达式中,操作符位于操作数之后,这种表达方式的优点式不需要括号来表示操作的优先级,从而简化了表达式的解析和计算

4.2 逆波兰表达式的特点

1.操作符在操作数之后:例如,普通的表达式 (中缀表达式)5 - 2 在逆波兰表达式中表示为 5 2 -

2.无需括号:由于操作符位置明确,不需要括号来表示优先级

3.易于计算:可以使用栈这种数据结构计算表达式的值

4.3 如何用栈解决逆波兰表达式求值

我们知道栈遵循后入先出(Last In First Out)的原则,逆波兰表达式中操作符位于操作数之后,我们可以根据这两个特点对元素进行出栈和入栈操作。

1.先初始化一个空栈

2.从左向右扫描表达式,如果遇到操作数就将其压入栈中,如果遇到操作符,就从栈中弹出两个元素,进行计算,并将计算的结果压入栈中

3.表达式扫描完成后,栈顶元素即为计算的结果

假设传递的参数是一个数组:tokens=["2","1","+","3","*"]

将其转为中缀表达式:(2+1)*3=9

代码编写:

public int evalRPN(String[] tokens) {Stack<Integer> stack = new Stack();for (int i = 0; i < tokens.length; i++) {String token = tokens[i];if (isNumber(token)) {stack.push(Integer.parseInt(token));} else {int num2 = stack.pop();int num1 = stack.pop();switch (token) {case "+":stack.push(num1 + num2);break;case "-":stack.push(num1 - num2);break;case "*":stack.push(num1 * num2);break;case "/":stack.push(num1 / num2);break;default:}}}return stack.pop();
}
public boolean isNumber(String token) {return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
}

http://www.lryc.cn/news/529468.html

相关文章:

  • 使用vhd虚拟磁盘安装两个win10系统
  • 代码随想录34 动态规划
  • 【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细)
  • Shell特殊状态变量以及常用内置变量总结
  • 【4Day创客实践入门教程】Day4 迈向高手之路——进一步学习!
  • EtherCAT-快速搭建
  • 【设计测试用例自动化测试性能测试 实战篇】
  • DBeaver连接MySQL提示Access denied for user ‘‘@‘ip‘ (using password: YES)的解决方法
  • 【MySQL — 数据库增删改查操作】深入解析MySQL的 Update 和 Delete 操作
  • 04树 + 堆 + 优先队列 + 图(D1_树(D1_基本介绍))
  • 【Proteus仿真】【51单片机】多功能计算器系统设计
  • Solon Cloud Gateway 开发:Route 的配置与注册方式
  • jstat命令详解
  • [Collection与数据结构] B树与B+树
  • Ubuntu 24.04 安装 NVIDIA Container Toolkit 全指南:让Docker拥抱GPU
  • 17.Word:李楠-学术期刊❗【29】
  • 图漾相机——C++语言属性设置
  • 【性能优化专题系列】利用CompletableFuture优化多接口调用场景下的性能
  • docker安装emqx
  • DeepSeek超越ChatGPT的能力及部分核心原理
  • Leetcode 3434. Maximum Frequency After Subarray Operation
  • 《DeepSeek-R1 问世,智能搜索领域迎来新变革》
  • GEE | 植被总初级生产力GPP的时间变化特征
  • 安卓(android)饭堂广播【Android移动开发基础案例教程(第2版)黑马程序员】
  • 本地部署DeepSeek
  • 赛博算卦之周易六十四卦JAVA实现:六幺算尽天下事,梅花化解天下苦。
  • Hive:窗口函数(1)
  • docker安装nacos2.2.4详解(含:nacos容器启动参数、环境变量、常见问题整理)
  • 基于PLC的变频调速系统设计
  • 鸿蒙开发在onPageShow中数据加载不完整的问题分析与解决