首页 > 编程语言 >【数据结构与算法】之栈详解

【数据结构与算法】之栈详解

时间:2024-10-24 17:17:34浏览次数:3  
标签:之栈 top 元素 栈顶 详解 操作 数据结构 data public

栈(Stack)是一种基本的线性数据结构,遵循后进先出、先进后出的原则。本文将更详细地介绍栈的概念、特点、Java 实现以及应用场景。

1. 栈概念概述

想象一摞叠放的盘子,你只能从最上面取盘子,放盘子也只能放在最上面。栈就像这样一摞盘子,它只有一个开口,称为栈顶 (Top)。另一端封闭,称为栈底 (Bottom)。元素的添加操作称为入栈 (Push),删除操作称为出栈 (Pop)

栈是一种抽象数据类型 (ADT),这意味着我们只关心它的操作和特性,而不关心具体的实现方式。栈的关键操作包括:

  • push(item): 将元素 item 添加到栈顶。

  • pop(): 移除并返回栈顶元素。

  • peek(): 返回栈顶元素,但不移除它。

  • isEmpty(): 检查栈是否为空。

  • size(): 返回栈中元素的数量 (部分实现可能不包含此方法)。

2. 栈的特点

  • 后进先出 (LIFO): 这是栈最核心的特点,最后入栈的元素总是最先出栈。

  • 操作受限: 只能在栈顶进行插入和删除操作,这限制了访问元素的灵活性,但也保证了操作的高效性 (时间复杂度为 O(1))。

  • 有序性: 栈维护了元素的插入顺序,虽然不像队列那样严格的 FIFO,但仍然保留了元素的相对顺序。

3. 栈的 Java 实现 

Java 中可以使用数组或链表实现栈。

3.1 基于数组的实现

public class ArrayStack<T> {
    private T[] data; // 存储栈元素的数组
    private int top; // 栈顶指针,指向栈顶元素的索引,-1 表示栈空
    private int capacity; // 栈的容量

    public ArrayStack(int capacity) {
        this.capacity = capacity;
        this.data = (T[]) new Object[capacity]; // 创建泛型数组
        this.top = -1; 
    }


    //判空
    public boolean isEmpty() {
        return top == -1;
    }

    
    //判满
    public boolean isFull() {
        return top == capacity - 1;
    }

    
    //入栈操作
    public void push(T item) {
        if (isFull()) {
            // 数组实现需要处理栈满的情况,可以抛出异常或进行扩容
            throw new RuntimeException("Stack is full"); 
            // 或者: resizeArray();  // 扩容操作 (此处未实现)
        }
        data[++top] = item; // 先将 top 指针加 1,再放入元素
    }


    //出栈操作
    public T pop() {
        if (isEmpty()) {
            throw new RuntimeException("Stack is empty");
        }
        T item = data[top]; // 获取栈顶元素
        data[top--] = null; //  为了避免对象游离,建议将出栈元素置为 null。先获取元素再将 top 指针减 1
        return item;
    }


    //返回栈顶元素
    public T peek() {
        if (isEmpty()) {
            throw new RuntimeException("Stack is empty");
        }
        return data[top];
    }


    //返回栈容量
    public int size() {
        return top + 1;
    }
}

3.2 基于链表的实现

public class LinkedListStack<T> {
    private Node<T> top; // 指向栈顶节点的指针

    private static class Node<T> { // 链表节点的内部类
        T data;
        Node<T> next;

        Node(T data) {
            this.data = data;
        }
    }

    public LinkedListStack() {
        this.top = null; // 初始化栈为空
    }

    public boolean isEmpty() {
        return top == null;
    }


    //入栈
    public void push(T item) {
        Node<T> newNode = new Node<>(item); // 创建新节点
        newNode.next = top; // 新节点指向原栈顶
        top = newNode; // 更新栈顶指针
    }


    //出战
    public T pop() {
        if (isEmpty()) {
            throw new RuntimeException("Stack is empty");
        }
        T data = top.data; // 获取栈顶元素
        top = top.next; // 更新栈顶指针
        return data;
    }


    //返回栈顶元素
    public T peek() {
        if (isEmpty()) {
            throw new RuntimeException("Stack is empty");
        }
        return top.data;
    }
}

4. 栈的基本操作图解

4.1 初始化栈

4.2 进栈

分别是:元素 a 进栈;元素 b、c 进栈

4.3 出栈

分别是:元素 c 出栈;b、a 出栈

4.4 获取栈顶元素

略,先判断栈是否空,不空直接返回 data[top] 即可。

5. 各个操作的时间复杂度

  • 入栈 (push): 数组实现: 均摊 O(1) (因为需要考虑扩容的情况,但大多数情况下是 O(1)),链表实现: O(1)

  • 出栈 (pop): O(1)

  • 查看栈顶元素 (peek): O(1)

6. 栈的局限性

  • 大小受限 (数组实现): 使用数组实现时,需要预先指定栈的大小。如果数据量超过预设大小,需要进行扩容,这会带来性能开销。链表实现则没有这个限制,可以动态增长。

  • 不灵活的访问方式: 只能访问栈顶元素,无法直接访问其他元素。如果需要访问其他元素,需要先将栈顶元素依次弹出。

7. 总结和应用场景 

栈是一种简单但强大的数据结构,其 LIFO 特性使其在许多场景下都非常有用,例如:

  • 函数调用栈: 程序执行函数时,会将函数的局部变量、参数、返回地址等信息存储在栈中。当函数执行完毕后,这些信息会从栈中弹出,程序回到调用函数的地方继续执行。递归函数的实现也依赖于函数调用栈。

  • 表达式求值: 例如,可以使用栈来计算后缀表达式 (逆波兰表达式)。

  • 浏览器历史记录: 浏览器的前进和后退按钮利用了栈的特性。点击后退按钮时,会从栈中弹出上一个访问的页面。

  • 撤销操作 (Undo/Redo): 许多软件的撤销操作使用了栈来存储操作的历史记录。每次执行操作时,将操作的信息压入栈中。点击撤销按钮时,从栈中弹出最近的操作并将其逆操作应用。

  • 深度优先搜索 (DFS): 图算法中常用的深度优先搜索算法使用栈来存储待访问的节点。

理解栈的概念和实现对于程序员来说至关重要,它能帮助我们更好地设计和优化程序。

希望这篇文章能帮助各位看官更好地理解栈这种重要的数据结构! 下期见,谢谢~

标签:之栈,top,元素,栈顶,详解,操作,数据结构,data,public
From: https://blog.csdn.net/weixin_64178283/article/details/143207007

相关文章

  • 【数据结构与算法】之队列详解
    队列(Queue)是一种重要的线性数据结构,遵循先进先出、后进后出的原则。本文将更详细地介绍队列的概念、特点、Java实现以及应用场景。模运算小复习:a%b的值总是小于b5%4=1  5 %2=11%5=1  4%5=41.队列概念概述想象一下排队买票,先排队的人总是先买......
  • 常用 Spring Boot 注解详解
    SpringBoot是一个基于Spring框架的工具集,旨在快速开发独立、生产级别的基于Spring的应用程序。它通过大量注解简化了配置和开发过程,下面将详细介绍一些常用的SpringBoot注解,包括它们的作用、实现原理、使用示例和注意事项。1.@SpringBootApplication作用这是一个......
  • 云渲染分布式渲染什么意思?一文详解
    渲染和分布式渲染是现代计算机图形学中的重要技术,它们通过将渲染任务分散到多个服务器或计算节点上,显著提高了渲染效率和处理大规模数据的能力。这项技术在动画制作、游戏开发和电影特效等领域发挥着关键作用,为创作者提供了更快速、更灵活的渲染解决方案。分布式渲染是什么意思?......
  • Python 文件与模块的运行顺序及调用时的执行流程详解【大白话版本!!】
    Python文件与模块的运行顺序及调用执行流程详解引言ython是一种强大的编程语言,具有极大的灵活性和简洁性。无论是在开发小型脚本,还是构建复杂的应用程序时,理解Python文件的运行顺序以及模块调用时的执行流程都至关重要。尤其当你开发大规模项目,涉及到多个模块(文件)之间......
  • python、JavaScript 、JAVA等实例代码演示教你如何免费获取股票数据(实时数据、历史数
    ​近一两年来,股票量化分析逐渐受到广泛关注。而作为这一领域的初学者,首先需要面对的挑战就是如何获取全面且准确的股票数据。因为无论是实时交易数据、历史交易记录、财务数据还是基本面信息,这些数据都是我们进行量化分析时不可或缺的宝贵资源。我们的核心任务是从这些数据......
  • HONEYWELL霍尼韦尔QCS系统5425400详解
    HONEYWELL霍尼韦尔QCS系统5425400是霍尼韦尔公司提供的一款高质量控制系统,该系统被广泛应用于多个工业领域,以下是关于该系统的详细介绍:一、系统概述HONEYWELL霍尼韦尔QCS系统5425400作为质量控制及系统的重要组成部分,具有高精度、高稳定性和易操作等特点。该系统采用先进的测......
  • 【C语言】自定义类型(结构体、枚举、联合的详解)
    写在前面今天是10月24日来到了一年一度的程序......
  • RSA算法详解及相关数学原理解析
    RSA算法详解及相关数学原理解析前言‍为了记录自己学习密码学的过程,也是为了便于个人应付相关课程的考核,故写此博客。本博客总结了怎么用C++手搓一个RSA算法,以及补补欠缺的一些数学知识和可能欠缺的一些其他算法的实现。参考了其他人的相关博客,用便于我自己理解的话和方式和......
  • 大二上 数据结构与算法笔记 20241024
    一.inline在C和C++编程语言中,inline关键字是一种函数修饰符,用于建议编译器在编译时将函数的代码直接插入到每个函数调用的地方,而不是进行常规的函数调用。这样做的目的是减少函数调用的开销,尤其是在函数体较小且调用频繁的情况下。作用和优点:减少函数调用开销:通过将函数......
  • 数据结构与算法——双链表的实现
    上次学习了单链表,这次来学习双链表。二者之间的区别是,单链表中的每个结点只存有后继结点的地址,而双链表中则存了两个地址,一个是前驱结点的地址,一个是后继结点的地址。结构体structListNode{ intelement;//数据域 structListNode*next; ......