线性数据结构之栈结构

news/2024/11/8 4:14:26 标签: 数据结构, java, 开发语言

栈(Stack)是一种线性数据结构,遵循“后进先出”(Last In, First Out,LIFO)的原则。栈主要有两种基本操作:push(入栈)和 pop(出栈)。在栈中,最新添加的元素最先被移除,类似于一摞盘子:放在最上面的盘子是最后放上去的,但却是最先拿走的。

栈的实现可以基于数组和链表,各有优缺点,适合不同的应用场景。下面将详细介绍栈的定义、特点、优缺点、应用场景以及用数组和链表实现栈的方式。

栈的基本概念

1. 特点

  • LIFO:栈遵循“后进先出”原则。
  • 基本操作
    • Push:将元素放入栈顶。
    • Pop:移除栈顶元素并返回其值。
    • Peek(Top):返回栈顶元素但不移除它。
    • isEmpty:检查栈是否为空。
    • Size:返回栈中元素的数量。

2. 优点

  • 操作简单:只允许在栈顶进行添加或删除操作。
  • 快速:只需要在栈顶操作,时间复杂度为 O(1)。

3. 缺点

  • 容量限制:基于数组的栈实现通常有固定容量,需要判断是否溢出。
  • 仅支持栈顶操作:无法直接访问中间元素。

4. 应用场景

  • 函数调用:程序执行时的调用栈,用来管理方法的调用和返回。
  • 表达式求值:如计算器中的中缀表达式转后缀表达式、表达式求值。
  • 撤销操作:用于实现文本编辑器、图片编辑器的撤销和重做功能。

一、数组实现栈

定义与实现原理

在数组实现的栈中,数组的最后一个位置作为栈顶。入栈时将元素添加到数组末尾,出栈时从数组末尾移除元素。数组实现的栈通常具有固定的大小,且在栈满时可能会引发溢出错误。

1. 优点

  • 访问速度快:数组在内存中是连续存储的,因此访问栈顶元素非常快。
  • 实现简单:操作简单,入栈和出栈仅涉及数组末尾元素。

2. 缺点

  • 固定大小:数组的大小在初始化时确定,不灵活。
  • 栈满溢出:当栈达到数组容量时,将无法继续入栈操作。

3. 适用场景

  • 静态数据栈:如数据量固定的递归栈、调用栈等。

4. 示例代码(Java)

java">class ArrayStack {
    private int[] stack;
    private int top;       // 栈顶指针
    private int capacity;  // 栈的最大容量

    // 构造函数,初始化栈
    public ArrayStack(int size) {
        stack = new int[size];
        top = -1;  // 栈为空时,top 指向 -1
        capacity = size;
    }

    // 入栈操作
    public void push(int value) {
        if (isFull()) {
            throw new StackOverflowError("栈已满,无法入栈");
        }
        stack[++top] = value;
    }

    // 出栈操作
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈为空,无法出栈");
        }
        return stack[top--];
    }

    // 返回栈顶元素,但不删除
    public int peek() {
        if (isEmpty()) {
            throw new RuntimeException("栈为空,无法查看栈顶");
        }
        return stack[top];
    }

    // 判断栈是否为空
    public boolean isEmpty() {
        return top == -1;
    }

    // 判断栈是否已满
    public boolean isFull() {
        return top == capacity - 1;
    }

    // 返回栈中元素个数
    public int size() {
        return top + 1;
    }
    
    // 打印栈元素
    public void display() {
        for (int i = 0; i <= top; i++) {
            System.out.print(stack[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        ArrayStack stack = new ArrayStack(5);
        stack.push(10);
        stack.push(20);
        stack.push(30);
        stack.display(); // 输出:10 20 30

        System.out.println("栈顶元素: " + stack.peek()); // 输出:栈顶元素: 30
        stack.pop();
        stack.display(); // 输出:10 20
    }
}

二、链表实现栈

定义与实现原理

链表实现的栈使用链表的头节点作为栈顶。每次入栈时在链表头部插入一个新节点,每次出栈时删除链表的头节点。这种实现方式可以动态扩展栈的大小。

1. 优点

  • 动态大小:链表实现的栈大小可以动态增长,不受固定容量限制。
  • 内存管理灵活:在需要的时候才分配内存,适合大型数据或频繁变化的数据。

2. 缺点

  • 内存开销大:链表节点需要存储数据和指针,内存消耗较大。
  • 访问速度慢:链表节点在内存中不连续,访问时间相对较长。

3. 适用场景

  • 动态数据栈:如大量动态数据的进出,或内存受限且数据量不确定的场景。

4. 示例代码(Java)

java">class LinkedListNode {
    int data;
    LinkedListNode next;

    LinkedListNode(int data) {
        this.data = data;
        this.next = null;
    }
}

class LinkedListStack {
    private LinkedListNode top; // 栈顶指针

    // 构造函数,初始化栈
    public LinkedListStack() {
        top = null;
    }

    // 入栈操作
    public void push(int value) {
        LinkedListNode newNode = new LinkedListNode(value);
        newNode.next = top; // 新节点指向当前的栈顶
        top = newNode;      // 栈顶指针指向新节点
    }

    // 出栈操作
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈为空,无法出栈");
        }
        int value = top.data;
        top = top.next; // 栈顶指针指向下一个节点
        return value;
    }

    // 返回栈顶元素,但不删除
    public int peek() {
        if (isEmpty()) {
            throw new RuntimeException("栈为空,无法查看栈顶");
        }
        return top.data;
    }

    // 判断栈是否为空
    public boolean isEmpty() {
        return top == null;
    }

    // 返回栈中元素个数
    public int size() {
        int count = 0;
        LinkedListNode current = top;
        while (current != null) {
            count++;
            current = current.next;
        }
        return count;
    }

    // 打印栈元素
    public void display() {
        LinkedListNode current = top;
        while (current != null) {
            System.out.print(current.data + " ");
            current = current.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        LinkedListStack stack = new LinkedListStack();
        stack.push(10);
        stack.push(20);
        stack.push(30);
        stack.display(); // 输出:30 20 10

        System.out.println("栈顶元素: " + stack.peek()); // 输出:栈顶元素: 30
        stack.pop();
        stack.display(); // 输出:20 10
    }
}

总结对比

实现方式优点缺点适用场景
数组实现栈访问速度快,适合小规模、静态数据的栈固定大小,可能导致溢出数据量固定、性能要求高的场景,如递归栈
链表实现栈动态大小,适合大量动态数据内存消耗较大,访问速度稍慢数据量不确定且变化频繁的场景,如动态堆栈
  • 数组实现的栈:适合数据量固定或可以预估的情况,比如递归函数调用栈。
  • 链表实现的栈:适合大量数据动态进出,或不确定数据量的情况,比如模拟浏览器前进后退操作栈。

http://www.niftyadmin.cn/n/5743213.html

相关文章

linux驱动-i2c子系统框架学习(1)

可以将整个 I2C 子系统用下面的框图来描述&#xff1a; 可以将上面这一 I2C 子系统划分为三个层次&#xff0c;分别为用户空间、内核空间和硬件层&#xff0c;内核空间就包括 I2C 设备驱动层、I2C 核心层和 I2C 适配器驱动层&#xff0c; 本篇主要内容就是介绍 I2C 子系统框架中…

Linux,shell基础,变量,数值运算

linux 一.shell基础1.什么是shell在linux内核与用户之间的解释器程序,通常指/bin/bash2.shell的使用方式1.交互式2.非交互式3.Bash基本特征1.快捷键2.历史命令3.重定向4.管道5.别名4.shell脚本1.规范脚本构成(1) #!指定解释器(2) # 注释信息(作者信息,步骤,思路,用途,变量等)(3…

i2c-tools 4.3 for Android 9.0

i2c-tools 4.3 Android 9.0下编译i2c-tools 4.3 下载源码 cd external wget https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/i2c-tools-4.3.tar.gz tar -zxvf i2c-tools-4.3.tar.gz 增加Android.mk Android.mk LOCAL_PATH : $(call my-dir)include $(C…

Proteus中数码管动态扫描显示不全(已解决)

文章目录 前言解决方法后记 前言 我是直接把以前写的 51 数码管程序复制过来的&#xff0c;当时看的郭天祥的视频&#xff0c;先送段选&#xff0c;消隐后送位选&#xff0c;最后来个 1ms 的延时。 代码在 Proteus 中数码管静态是可以的&#xff0c;动态显示出了问题——显示…

[spring源码]spring配置类解析

解析配置类 在启动Spring时&#xff0c;需要传入一个AppConfig.class给ApplicationContext&#xff0c;ApplicationContext会根据AppConfig类封装为一个BeanDefinition&#xff0c;这种BeanDefinition我们把它称为配置类BeanDefinition AnnotationConfigApplicationContext a…

Vue3版本的uniapp项目运行至鸿蒙系统

新建Vue3版本的uniapp项目 注意&#xff0c;先将HbuilderX升级至最新版本&#xff0c;这样才支持鸿蒙系统的调试与运行&#xff1b; 按照如下图片点击&#xff0c;快速升级皆可。 通过HbuilderX创建 官方文档指导链接 点击HbuilderX中左上角文件->新建->项目 创建vue3…

Git通讲-第二章(2):对象数据库

前言 理解了上篇文章的两大模型&#xff08;快照和不可变对象&#xff09;后&#xff0c;让我们看看Git 的核心——对象数据库&#xff0c;快照存储在 .git/objects 目录中&#xff0c;Git 通过这种方式管理项目的所有历史和数据。 Git对象数据库 下面是 .git/objects 目录的…

ac8257 android 9 lk upgrade升级后分区表错误问题

问题描述 ac8257 Android 9&#xff0c;使用lk upgrade升级功能升级固件&#xff0c;当分区表发生变化时&#xff0c;分区表会出现以下问题&#xff1a; 1、备份分区表错误 2、分区表存在重叠 验证方法 lk upgrade升级后&#xff0c;用sgdisk命令检测分区表是否存在错误。…