0

0

java代码怎样用数组实现顺序栈 java代码顺序栈结构的实用实现教程​

蓮花仙者

蓮花仙者

发布时间:2025-08-12 21:23:01

|

405人浏览过

|

来源于php中文网

原创

数组实现顺序栈的核心原因是其访问效率高、内存连续、实现简单,适合数据规模可预估且对性能要求高的场景;1. 数组通过索引直接访问栈顶元素,时间复杂度为o(1),具备良好的缓存局部性;2. 其固定容量的局限性可通过动态扩容、预分配、错误处理或改用链表等策略应对;3. 实际应用包括函数调用模拟、括号匹配、表达式求值、浏览器前进后退、文本编辑器撤销重做及深度优先搜索等,均依赖栈的后进先出特性;4. 动态扩容虽常用但非唯一方案,需根据性能、内存和业务需求权衡选择最适合的实现方式。

java代码怎样用数组实现顺序栈 java代码顺序栈结构的实用实现教程​

通过Java代码使用数组实现顺序栈,核心思路是利用一个固定大小的数组来存储栈元素,并用一个整数变量来追踪栈顶的位置。当元素入栈时,栈顶指针上移;出栈时,栈顶指针下移。这种实现方式简洁直观,但在容量管理上需要额外考虑。

解决方案

import java.util.EmptyStackException; // Java标准库中的空栈异常,用于增强代码可读性

/**
 * 一个基于数组实现的顺序栈。
 * 泛型设计,使其可以存储任何类型的对象。
 */
public class SeqStack {
    private Object[] data; // 存储栈元素的数组
    private int top;       // 栈顶指针,指向栈顶元素的位置(如果栈为空,通常为-1)
    private static final int DEFAULT_CAPACITY = 10; // 默认初始容量

    /**
     * 构造一个具有默认容量的顺序栈。
     */
    public SeqStack() {
        this(DEFAULT_CAPACITY);
    }

    /**
     * 构造一个具有指定容量的顺序栈。
     * @param capacity 栈的初始容量
     * @throws IllegalArgumentException 如果容量小于等于0
     */
    public SeqStack(int capacity) {
        if (capacity <= 0) {
            throw new IllegalArgumentException("栈的容量必须大于0");
        }
        this.data = new Object[capacity];
        this.top = -1; // 初始时栈为空,top指向-1
    }

    /**
     * 将元素压入栈顶。
     * @param element 要压入的元素
     * @throws IllegalStateException 如果栈已满
     */
    public void push(E element) {
        if (isFull()) {
            // 实际应用中,这里可能会选择扩容而不是直接抛异常
            throw new IllegalStateException("栈已满,无法压入新元素。");
        }
        data[++top] = element; // 栈顶指针先加1,再存入元素
    }

    /**
     * 弹出栈顶元素并返回。
     * @return 弹出的栈顶元素
     * @throws EmptyStackException 如果栈为空
     */
    @SuppressWarnings("unchecked") // 忽略类型转换警告
    public E pop() {
        if (isEmpty()) {
            throw new EmptyStackException(); // 栈为空,无法弹出
        }
        E element = (E) data[top]; // 获取栈顶元素
        data[top--] = null;       // 将原栈顶位置置为null,帮助GC,然后栈顶指针减1
        return element;
    }

    /**
     * 查看栈顶元素,但不将其弹出。
     * @return 栈顶元素
     * @throws EmptyStackException 如果栈为空
     */
    @SuppressWarnings("unchecked")
    public E peek() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }
        return (E) data[top]; // 直接返回栈顶元素
    }

    /**
     * 检查栈是否为空。
     * @return 如果栈为空则返回true,否则返回false
     */
    public boolean isEmpty() {
        return top == -1;
    }

    /**
     * 检查栈是否已满。
     * @return 如果栈已满则返回true,否则返回false
     */
    public boolean isFull() {
        return top == data.length - 1;
    }

    /**
     * 返回栈中元素的数量。
     * @return 栈中元素的数量
     */
    public int size() {
        return top + 1;
    }

    /**
     * 返回栈的容量。
     * @return 栈的容量
     */
    public int capacity() {
        return data.length;
    }

    // 简单测试
    public static void main(String[] args) {
        SeqStack stringStack = new SeqStack<>(3);
        System.out.println("栈是否为空? " + stringStack.isEmpty()); // true

        stringStack.push("A");
        stringStack.push("B");
        stringStack.push("C");
        System.out.println("当前栈大小: " + stringStack.size()); // 3
        System.out.println("栈顶元素: " + stringStack.peek()); // C
        System.out.println("栈是否已满? " + stringStack.isFull()); // true

        try {
            stringStack.push("D"); // 尝试压入第四个元素,会抛异常
        } catch (IllegalStateException e) {
            System.out.println("尝试压入D时捕获到异常: " + e.getMessage());
        }

        System.out.println("弹出: " + stringStack.pop()); // C
        System.out.println("弹出: " + stringStack.pop()); // B
        System.out.println("当前栈大小: " + stringStack.size()); // 1

        System.out.println("弹出: " + stringStack.pop()); // A
        System.out.println("栈是否为空? " + stringStack.isEmpty()); // true

        try {
            stringStack.pop(); // 尝试从空栈弹出,会抛异常
        } catch (EmptyStackException e) {
            System.out.println("尝试从空栈弹出时捕获到异常: 栈为空");
        }
    }
}

为什么选择数组实现顺序栈?它有哪些实际考量?

在我看来,选择数组来实现顺序栈,最直接的原因就是它的简单性和内存效率。数组在内存中是连续存储的,这意味着访问栈中的任何元素(尤其是栈顶元素)都非常快,是O(1)的时间复杂度。这种直接的索引访问,加上良好的CPU缓存局部性,使得顺序栈在处理大量数据且对性能有较高要求的场景下表现出色。比如,如果你需要一个临时的、快速存取且大小相对固定的数据集合,数组实现的栈无疑是一个非常好的选择。

然而,凡事都有两面性。数组实现的最大局限性在于其固定容量。一旦初始化,它的存储空间就确定了。这意味着如果你预估的容量过小,栈很快就会“满”,导致无法再压入新元素,这在编程中通常表现为

StackOverflowError
(当然,我们这里是自定义抛出
IllegalStateException
)。反之,如果容量设置得过大,又会造成内存的浪费。所以,在决定使用数组实现顺序栈时,一个关键的实际考量就是你对数据规模的预估能力。如果你能比较准确地知道栈的最大可能大小,或者它是一个短期、局部使用的辅助结构,那么数组实现会非常高效。但如果数据量波动大、难以预测,或者需要长时间运行且可能无限增长,那么固定大小的数组就显得力不从心了。

立即学习Java免费学习笔记(深入)”;

顺序栈在实际项目中能解决哪些问题?

顺序栈虽然看似基础,但在许多实际编程场景中都扮演着重要的角色。我个人觉得它最典型的应用,就是对函数调用堆栈的模拟。虽然JVM本身有其复杂的调用栈机制,但在某些特定场景下,比如实现自己的解释器、编译器中的语法分析,或者需要手动管理函数调用的上下文时,顺序栈就能派上用场。

Faceswap
Faceswap

免费开源的AI换脸工具

下载

除了这个,它在解决一些算法问题时也特别顺手:

  1. 括号匹配与表达式求值:这是栈的经典应用。无论是检查代码中的括号是否正确闭合
    ([])
    ,还是将中缀表达式转换为后缀表达式,再进行求值,栈都提供了非常优雅的解决方案。每次遇到左括号就入栈,遇到右括号就检查栈顶是否为对应的左括号,如果不是或栈为空,则说明不匹配。
  2. 浏览器前进/后退功能:这其实是两个栈的组合应用。一个栈存储“后退”的历史页面,另一个栈存储“前进”的历史页面。当你点击“后退”时,当前页面从“前进”栈弹出,压入“后退”栈;点击“前进”时,操作反之。
  3. 文本编辑器的撤销(Undo)/重做(Redo)功能:每次用户执行一个可撤销的操作(如输入文字、删除行),就把该操作及其相关数据压入一个“撤销栈”。当用户点击“撤销”时,从撤销栈弹出一个操作并执行其逆操作,同时将该操作压入“重做栈”。
  4. 深度优先搜索 (DFS):在图或树的遍历中,非递归的DFS算法通常会使用一个栈来保存待访问的节点。每次从栈顶取出一个节点访问,然后将其未访问的邻居节点压入栈中。

这些例子都体现了栈“后进先出”的特性,它能很好地帮助我们管理操作的顺序或状态的上下文。

如何处理顺序栈的容量限制?动态扩容是唯一选择吗?

处理顺序栈的容量限制,这确实是数组实现栈时一个绕不开的话题。最常见的,也是Java标准库

ArrayList
Vector
java.util.Stack
内部用的就是
Vector
)所采用的策略,就是动态扩容。当
push
操作发现栈已满时,它会创建一个更大的新数组(通常是原容量的1.5倍或2倍),然后将旧数组中的所有元素复制到新数组中,最后将
data
引用指向新数组。这样做的好处是,从摊还分析的角度来看,每次
push
操作的平均时间复杂度仍然是O(1),虽然偶尔会遇到O(n)的复制操作,但长期来看效率很高。

然而,动态扩容并非总是唯一或最佳选择。在某些特定场景下,我们可能需要考虑其他策略:

  1. 预估与固定容量:如果你的应用场景中,栈的最大容量是已知或可以合理预估的,那么最简单直接的方式就是在初始化时就分配足够大的容量。例如,如果你确定某个算法最多只需要处理100个层级的递归,那么直接创建一个容量为100的栈就足够了。这样可以避免扩容带来的额外开销和内存碎片化问题。
  2. 错误处理与拒绝服务:在一些对资源消耗极其敏感,或者对“满栈”有明确业务边界的系统中,当栈满时,我们可能选择直接抛出异常(就像我们示例代码中那样
    IllegalStateException
    ),或者返回一个表示失败的状态码。这是一种“拒绝服务”的策略,它将容量管理的问题抛给调用者,由调用者来决定如何应对。这在某些嵌入式系统或资源受限环境中尤为常见。
  3. 基于链表的栈:如果容量问题是一个持续的痛点,并且你对内存连续性或缓存局部性没有那么极致的要求,那么使用链表来实现栈(比如
    java.util.LinkedList
    可以作为
    Deque
    来实现栈的功能)是一个非常好的替代方案。链表实现的栈理论上没有容量限制(只受限于系统内存),每次
    push
    pop
    都只是创建或销毁一个节点并调整指针,时间复杂度稳定在O(1),且不会有复制整个数组的开销。缺点是内存开销相对略大,因为每个节点都需要额外的指针存储空间,并且由于内存不连续,缓存命中率可能不如数组。

所以,选择哪种策略,很大程度上取决于你对性能、内存、以及对“栈满”情况的业务容忍度的具体权衡。没有银弹,只有最适合你当前场景的方案。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

396

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

575

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

396

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

575

2023.08.10

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

407

2023.08.14

linux是嵌入式系统吗
linux是嵌入式系统吗

linux是嵌入式系统,是一种用途广泛的系统软件,其特点是:1、linux系统是完全开放、免费的;2、linux操作系统的显著优势是多用户和多任务,保证了多个用户使用互不影响;3、设备是独立的,只要安装驱动程序,任何用户都可以对任意设备进行使用和操作。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

172

2024.02.23

C++ 嵌入式系统开发入门与实践
C++ 嵌入式系统开发入门与实践

本专题将带你系统掌握 C++ 在嵌入式系统中的实战应用,内容覆盖硬件抽象、驱动开发、内存与性能优化、实时系统编程、跨平台编译构建,以及常用嵌入式框架与调试技巧,帮助开发者从零构建可运行于 MCU、ARM 等平台的高性能嵌入式项目。

215

2025.11.18

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

141

2026.01.28

包子漫画在线官方入口大全
包子漫画在线官方入口大全

本合集汇总了包子漫画2026最新官方在线观看入口,涵盖备用域名、正版无广告链接及多端适配地址,助你畅享12700+高清漫画资源。阅读专题下面的文章了解更多详细内容。

24

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Git 教程
Git 教程

共21课时 | 3.1万人学习

Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号