数据结构-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()是获取栈顶元素并返回,不进行出栈操作

常用方法的代码演示:

java 复制代码
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");
        //获取栈中有效元素的个数-->4
        System.out.println(stack.size());
        //获取栈顶元素-->hajimi  遵循后进先出
        System.out.println(stack.peek());
        //peek方法不会改变栈中的元素个数
        System.out.println(stack.size());
        //出栈
        String deleteElem=stack.pop();
        //获取栈中有效元素的个数-->3
        System.out.println(stack.size());
        //判断栈是否为空-->false
        System.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"。

代码编写:

java 复制代码
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;
    }
}

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

java 复制代码
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

代码编写:

java 复制代码
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));
}
相关推荐
假客套13 分钟前
Java小白入门教程:类和对象
java·开发语言
hhmy1234561 小时前
表格结构标签
java·服务器·前端
智客工坊1 小时前
AI编程助手带来的洞察和启发——程序员职业的变革
java·javaweb·管理
依恋、阳光1 小时前
java的Stream流
java·开发语言·数据结构·stream
来恩10031 小时前
Java 性能优化与新特性
java
墨绿色的摆渡人1 小时前
python | OpenCV小记(一):cv2.imread(f) 读取图像操作(待更新)
开发语言·python·opencv
小林熬夜学编程2 小时前
【Python】第七弹---Python基础进阶:深入字典操作与文件处理技巧
开发语言·前端·python·算法
明月看潮生2 小时前
青少年编程与数学 02-008 Pyhon语言编程基础 05课题、数据类型
开发语言·python·青少年编程·数据类型·编程与数学
customer082 小时前
【开源免费】基于SpringBoot+Vue.JS美食推荐商城(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
来恩10032 小时前
Java 分布式与微服务架构:现代企业应用开发的新范式
java·分布式·架构