当前位置: 代码网 > it编程>编程语言>Java > Java中的Stack(栈)(如果想知道Java中有关Stack的知识点,那么只看这一篇就足够了!)

Java中的Stack(栈)(如果想知道Java中有关Stack的知识点,那么只看这一篇就足够了!)

2024年07月28日 Java 我要评论
Java中的Stack(栈)(如果想知道Java中有关Stack的知识点,那么只看这一篇就足够了!)

        前言:栈(stack)是一种基础且重要的数据结构,以其后进先出(lifo, last in first out)的特性广泛应用于计算机科学和编程中。


先让我们看一下本文大致的讲解内容:

目录

1.栈的初识

2.栈的自我实现

(1)数组实现:

(2)链表实现

3.栈中常用api

4.栈的应用场景

5.总结


1.栈的初识

        在开始学习使用栈之前,先让我们来了解一下什么是栈:

栈的主要特性包括:

        如果使用我们日常生活中的案例来解释的话,就如同子弹弹夹,先装入的子弹后被打出,后装入的子弹,先被打出:

将其转换为编程语言图像(如图):

其中:

        ——压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

        ——出栈:栈的删除操作叫做出栈。出数据在栈顶。

通过上述的讲解,这样我们就大致的了解了什么是栈(stack)了!

2.栈的自我实现

        学习完了什么是栈之后,然我们试试能不能使用已有的知识体系来实现栈,在java中自我实现栈的方式大致有两种:使用数组实现与使用链表实现

(1)数组实现:

public class arraystack {
    public int[] stack; // 用于存储栈元素的数组
    public int top; // 栈顶索引

    public arraystack(int size) {
        stack = new int[size]; // 初始化数组大小
        top = -1; // 初始化栈顶索引为-1,表示栈为空
    }

    // 将元素压入栈顶
    public void push(int value) {
        if (top == stack.length - 1) {
            throw new stackoverflowerror("stack is full"); // 如果栈已满,抛出异常
        }
        stack[++top] = value; // 将元素压入栈顶,并更新栈顶索引
    }

    // 弹出栈顶元素
    public int pop() {
        if (top == -1) {
            throw new emptystackexception(); // 如果栈为空,抛出异常
        }
        return stack[top--]; // 返回栈顶元素,并更新栈顶索引
    }

    // 返回栈顶元素但不弹出
    public int peek() {
        if (top == -1) {
            throw new emptystackexception(); // 如果栈为空,抛出异常
        }
        return stack[top]; // 返回栈顶元素
    }

    // 检查栈是否为空
    public boolean isempty() {
        return top == -1; // 如果栈顶索引为-1,表示栈为空
    }
}

现在我们再回顾一下上述代码的逻辑:

        1. 首先先定义了一个基于数组实现的栈类 arraystack。它包含一个用于存储栈元素的数组 stack 和一个指示栈顶位置的变量 top
        2. 然后使用构造方法 arraystack(int size) 来初始化栈的大小,并将 top 设置为 -1。

        3. push(int value) 方法将元素压入栈顶,如果栈满则抛出 stackoverflowerrorpop() 方法弹出栈顶元素,如果栈为空则抛出 emptystackexceptionpeek() 方法返回栈顶元素但不弹出,如果栈为空则抛出 emptystackexceptionisempty() 方法检查栈是否为空。

(2)链表实现

public class doublylinkedliststack {
    public node head; // 链表头节点
    public node tail; // 链表尾节点

    // 定义节点类
    private static class node {
        int value; // 节点值
        node next; // 指向下一个节点的指针
        node prev; // 指向上一个节点的指针

        node(int value) {
            this.value = value; // 初始化节点值
            this.next = null; // 初始化下一个节点指针为空
            this.prev = null; // 初始化前一个节点指针为空
        }
    }

    public doublylinkedliststack() {
        head = null; // 初始化头节点为空
        tail = null; // 初始化尾节点为空
    }

    // 将元素压入栈顶
    public void push(int value) {
        node newnode = new node(value); // 创建新节点
        if (tail == null) {
            head = tail = newnode; // 如果链表为空,头尾节点都指向新节点
        } else {
            tail.next = newnode; // 将新节点连接到链表尾部
            newnode.prev = tail; // 新节点的前驱指向当前尾节点
            tail = newnode; // 更新尾节点为新节点
        }
    }

    // 弹出栈顶元素
    public int pop() {
        if (tail == null) {
            throw new emptystackexception(); // 如果栈为空,抛出异常
        }
        int value = tail.value; // 获取尾节点的值
        if (tail.prev == null) {
            head = tail = null; // 如果只有一个元素,清空链表
        } else {
            tail = tail.prev; // 更新尾节点为前驱节点
            tail.next = null; // 断开新尾节点的next指针
        }
        return value; // 返回弹出的值
    }

    // 返回栈顶元素但不弹出
    public int peek() {
        if (tail == null) {
            throw new emptystackexception(); // 如果栈为空,抛出异常
        }
        return tail.value; // 返回尾节点的值
    }

    // 检查栈是否为空
    public boolean isempty() {
        return tail == null; // 如果尾节点为空,栈为空
    }
}

现在我们再回顾一下上述代码的逻辑:

        首先我们先定义了一个基于双向链表实现的栈类 doublylinkedliststack。它包含头节点和尾节点,使用内部的 node 类表示每个节点。然后我们实现了一下主要的方法包括:

  • push(int value):将新元素压入栈顶。

  • pop():弹出栈顶元素,如果栈为空则抛出异常。

  • peek():返回栈顶元素但不弹出,若栈为空则抛出异常。

  • isempty():检查栈是否为空,返回布尔值。

这样我们就用现有的知识体现来实现了栈(stack)了!

3.栈中常用api

        学习完了自我实现栈(stack)之后,现在让我们看看java中自带的stack如何使用吧:

栈中常用的方法主要包括以下几种:

现在让我们使用一个实例来进行讲解:

import java.util.stack;

public class stackexample {
    public static void main(string[] args) {
        //创建一个栈
        stack<integer> stack = new stack<>();

        // 压入栈顶
        stack.push(1);
        stack.push(2);
        stack.push(3);

        // 查看栈顶元素
        system.out.println("栈顶元素: " + stack.peek());

        // 弹出栈顶元素
        system.out.println("弹出栈顶元素: " + stack.pop());

        // 查看栈顶元素
        system.out.println("栈顶元素: " + stack.peek());

        // 检查栈是否为空
        system.out.println("栈是否为空: " + stack.isempty());
    }
}

——这样我们就大致的了解了在java中如何去操作一个创建出来的栈了!

4.栈的应用场景

        学习完栈之后,读者可能会发出疑问,栈到底有什么用处呢?栈在实际应用中有许多场景,下面列举几个典型的应用:

  1. 表达式求值:如中缀表达式转后缀表达式的计算。
  2. 函数调用:栈用于保存函数调用过程中的局部变量和返回地址。
  3. 括号匹配:用于检查括号是否成对匹配。
  4. 浏览器的前进后退:使用栈保存历史页面,以便用户前进和后退。
  5. 深度优先搜索(dfs):在图或树的遍历中使用。

        这里我们只举出第一个(表达式求值)来讲解(其他读者如果有兴趣进一步学习,可以自行查找学习!

表达式求值即为:中缀表达式转换为后缀表达式(也称逆波兰表达式),以下为百度的解释:

其作用主要体现在以下几个方面:

  1. 简化计算:后缀表达式不需要括号,操作符和操作数的顺序明确,计算时更简单。

  2. 便于计算机处理:计算机处理后缀表达式更高效,避免了运算优先级的复杂性。

  3. 支持堆栈算法:后缀表达式可以直接使用栈来进行求值,适合实现逆波兰表示法。

  4. 提高表达式解析速度:在编译器和解释器中,后缀表达式有助于快速解析和执行表达式。

了解完了什么是逆波兰表达式之后,我们使用一道例题来进行讲解:

题目链接:----->. - 力扣(leetcode)

解题的大致思路为:

import java.util.stack;

public class postfixevaluation {
    public static int evaluate(string expression) {
        stack<integer> stack = new stack<>(); // 创建一个栈用于存储操作数

        // 遍历后缀表达式中的每个字符
        for (char c : expression.tochararray()) {
            if (character.isdigit(c)) {
                // 如果是数字,将其压入栈中
                stack.push(c - '0');
            } else {
                // 如果是操作符,从栈中弹出两个操作数
                int b = stack.pop(); // 弹出栈顶元素作为右操作数
                int a = stack.pop(); // 弹出下一个元素作为左操作数
                
                // 根据操作符进行计算,并将结果压入栈中
                switch (c) {
                    case '+':
                        stack.push(a + b);
                        break;
                    case '-':
                        stack.push(a - b);
                        break;
                    case '*':
                        stack.push(a * b);
                        break;
                    case '/':
                        stack.push(a / b);
                        break;
                }
            }
        }
        return stack.pop(); // 返回栈顶元素,即最终结果
    }

    public static void main(string[] args) {
        string expression = "231*+9-"; // 后缀表达式示例
        system.out.println("后缀表达式求值结果: " + evaluate(expression)); // 输出结果
    }
}

这样我们就大致的理解了栈的用处了!

5.总结

        栈是一种基础且重要的数据结构,它的后进先出特性在许多应用场景中发挥了重要作用。在java中,栈可以通过数组和链表实现,java.util.stack类也提供了现成的栈实现。

        通过理解栈的基本概念和常用方法,以及掌握如何使用双向链表自我实现栈,我们可以在实际编程中更加灵活地运用这一数据结构。栈在表达式求值、函数调用、括号匹配、浏览器前进后退和深度优先搜索等方面都有广泛的应用,熟练掌握栈的使用将极大提高我们的编程能力。


以上就是本篇文章的全部内容了~~~

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com