【数据结构】栈(基于数组、链表实现 + GIF图解 + 原码)

Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
🌱🌱个人主页:奋斗的明志
🌱🌱所属专栏:数据结构

在这里插入图片描述

📚本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。

在这里插入图片描述

文章目录

  • 前言
  • 一、栈(Stack)
    • 1.概念
    • 2.栈在现实生活中的例子
  • 二、栈的使用
    • 1.方法
    • 2.代码
  • 三、栈的模拟实现
    • 1.入栈图解
    • 2.出栈图解
    • 3.数组实现的栈
    • 4.链表实现的栈
    • 5.push(链表实现)
    • 6.pop(链表实现)
  • 四、栈的应用场景
    • 1.改变元素的序列
    • 2.将递归转化为循环
      • 2.1 递归方式
      • 2.2 循环方式
  • 五、了解中缀表达式、后缀表达式
  • 总结


前言

一、栈(Stack)

1.概念

  • :一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈 顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO( Last In First Out)的原则。
  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
  • 出栈:栈的删除操作叫做出栈。 出数据在栈顶
  • 栈顶栈底: 这个描述是偏向于逻辑上的内容,因为大家知道数组在末尾插入删除更容易,而单链表通常在头插入删除更容易。所以数组可以用末尾做栈顶,而链表可以头做栈顶

2.栈在现实生活中的例子

在这里插入图片描述

在这里插入图片描述

栈的应用广泛,比如你的程序执行查看调用堆栈、计算机四则加减运算、算法的非递归形式、括号匹配问题等等。所以栈也是必须掌握的一门数据结构。最简单大家都经历过,你拿一本书上下叠在一起,就是一个后进先出的过程,你可以把它看成一个栈。下面我们介绍数组实现的栈链表实现的栈

二、栈的使用

1.方法

方法功能
Stack()构造一个空的栈
E push(E e)将e入栈,并返回e
E pop()将栈顶元素出栈并返回
E peek()获取栈顶元素
int size()获取栈中有效元素个数
boolean empty()检测栈是否为空

2.代码

代码如下(示例):

public static void main(String[] args) {Stack<Integer> s = new Stack();s.push(1);s.push(2);s.push(3);s.push(4);System.out.println(s.size());  // 获取栈中有效元素个数---> 4 System.out.println(s.peek());  // 获取栈顶元素---> 4s.pop();  // 4出栈 ,栈中剩余1   2   3 ,栈顶元素为3System.out.println(s.pop());  // 3出栈 ,栈中剩余1  2  栈顶元素为3 if(s.empty()){if (s.empty()) {System.out.println("栈空");} else {System.out.println(s.size());}
}

三、栈的模拟实现

在这里插入图片描述
从上图中可以看到, Stack继承了Vector VectorArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的。

1.入栈图解

在这里插入图片描述

2.出栈图解

在这里插入图片描述

3.数组实现的栈

代码如下(示例):

package stackdemo;import java.util.Arrays;public class MyStack {//用什么来组织呢?// 数组、链表// 目前先用数组//先创建数组public int[] elem;public int usedSize;//表示有效个数,也可以当下标使用public static final int DEFAULT_CAPACITY = 10;public MyStack() {//初始化数组容量this.elem = new int[DEFAULT_CAPACITY];}//压栈  入栈public void push(int val){if (isFull()){//扩容this.elem = Arrays.copyOf(elem, 2 * elem.length);}elem[usedSize++] = val;}/*** 判断数组是否满了*/public boolean isFull(){return usedSize == this.elem.length;}/*** 出栈* @return*/public int pop(){if (isEmpty()){throw new EmptyStackException("栈为空");}usedSize--;return elem[usedSize];}public boolean isEmpty(){return usedSize == 0;}public int peek(){if (isEmpty()){throw new EmptyStackException("栈为空");}return elem[usedSize - 1];}
}

测试类

import stackdemo.MyStack;import java.util.LinkedList;
import java.util.Stack;public class Test {public static void main(String[] args) {LinkedList<Integer> stack = new LinkedList<>();stack.push(1);stack.push(2);stack.push(3);System.out.println(stack.pop());System.out.println(stack.peek());}public static void main01(String[] args) {
//        Stack<Integer> stack = new Stack<>();MyStack stack = new MyStack();//向栈里面添加元素stack.push(12);stack.push(23);stack.push(34);stack.push(45);//先进后出//出栈,有两个方法// pop 弹出,有一个返回值 直接从栈里面删除元素int ret = stack.pop();System.out.println(ret);//45// peek 也有返回值// peek 只是获取栈顶元素 ,不删除// 元素还在栈里面int peek = stack.peek();System.out.println(peek);// 判断栈空不空System.out.println(stack.isEmpty());}
}

4.链表实现的栈

像数组那样在尾部插入删除。大家都知道链表效率低在查询,而查询到尾部效率很低,就算用了尾指针,可以解决尾部插入效率,但是依然无法解决删除效率(删除需要找到前驱节点),还需要双向链表。前面虽然详细介绍过双向链表,但是这样未免太复杂!

所以我们先采用带头节点的单链表在头部插入删除,把头当成栈顶,插入直接在头节点后插入,删除也直接删除头节点后第一个节点即可,这样就可以完美的满足栈的需求。

代码如下(示例):

package stackdemo;public class lisStack<T> {static class Node<T> {T data;Node next;public Node() {}public Node(T value) {this.data = value;}}int length;Node<T> head;//头节点public lisStack() {head = new Node<>();length = 0;}boolean isEmpty() {return head.next == null;}int length() {return length;}public void push(T value) {//近栈Node<T> team = new Node<T>(value);if (length == 0) {head.next = team;} else {team.next = head.next;head.next = team;}length++;}public T peek() throws Exception {if (length == 0) {throw new Exception("链表为空");} else {//删除return (T) head.next.data;}}public T pop() throws Exception {//出栈if (length == 0) {throw new Exception("链表为空");} else {//删除T value = (T) head.next.data;head.next = head.next.next;//va.nextlength--;return value;}}public String toString() {if (length == 0) {return "";} else {String va = "";Node team = head.next;while (team != null) {va += team.data + " ";team = team.next;}return va;}}
}

5.push(链表实现)

push插入

与单链表头插入一致,如果不太了解可以看看前面写的线性表有具体讲解过程。

和数组形成的栈有个区别,链式实现的栈理论上栈没有大小限制(不突破内存系统限制),不需要考虑是否越界,而数组则需要考虑容量问题。

  • 如果一个节点team入栈:
  • 空链表入栈head.next=team;
  • 非空入栈team.next=head.next;head.next=team;

在这里插入图片描述

6.pop(链表实现)

pop弹出

与单链表头删除一致,如果不太了解请先看前面单链表介绍的。

和数组同样需要判断栈是否为空,如果节点team出栈:head指向team后驱节点。

在这里插入图片描述

四、栈的应用场景

1.改变元素的序列

  1. 若进栈序列为 1,2,3,4 ,进栈过程中可以出栈 ,则下列不可能的一个出栈序列是 ()

    A: 1,4,3,2
    B: 2,3,4,1
    C: 3,1,4,2
    D: 3,4,2,1

  2. 一个栈的初始状态为空。现将元素1、2、3、4、5、A、 B、C、 D、 E依次入栈 ,然后再依次出栈 ,则元素出栈的顺 序是( )。

    A: 12345ABCDE
    B: EDCBA54321
    C: ABCDE12345
    D: 54321EDCBA

2.将递归转化为循环

2.1 递归方式

思路解析:

  • 如果 head 不为 null,递归调用 printList(head.next) 先递归到链表的末尾。
  • 当递归回溯时,打印当前节点 head 的值。

工作原理:

  • 当 printList(head.next) 运行到链表末尾时,开始逐层回溯。
  • 每次回溯时,会依次打印每个节点的值,实现了链表的逆序输出。
// 递归方式
void printList(Node head) {if (null != head) {printList(head.next);System.out.print(head.val + " ");}
}

2.2 循环方式

思路解析:

  • 如果 head 为 null,直接返回。
  • 使用一个栈 s 来存储链表中的节点。
  • 遍历链表,将每个节点依次压入栈中。
  • 最后,依次弹出栈中的节点并打印其值,实现了链表的逆序输出。

工作原理:

  • 遍历链表的过程中,将节点依次压入栈中,因为栈的特性是后进先出(LIFO)。
  • 当遍历完成后,栈中的节点顺序是链表的逆序。
  • 依次弹出栈中的节点并打印,即可实现链表元素值的逆序输出。
// 循环方式
void printList(Node head) {if (null == head) {return;}Stack<Node> s = new Stack<>();
// 将链表中的结点保存在栈中Node cur = head;while (null != cur) {s.push(cur);cur = cur.next;}// 将栈中的元素出栈while (!s.empty()) {System.out.print(s.pop().val + " ");}
}

五、了解中缀表达式、后缀表达式

  • 下面以 a + b * c + ( d * e + f ) * g 为例子

  • 讲下应该怎么把中缀表达式转换成后缀表达式。

  • 按先加减后乘除的原则给表达式加括号

  • 结果:((a+(bc))+(((de)+f)*g))

  • 由内到外把每个括号里的表达式换成后缀

  • 最终结果:a b c * + d e * f + g * +

  • 这样就得到了中缀表达式转后缀表达式的最终结果。

  • 此法应付考试有神效。

总结

LinkedKist 就可以当做栈来使用

  • 递归方式:简单、优雅,但可能会面临栈溢出的风险,特别是在链表非常长的情况下。
  • 循环方式:使用了额外的栈来辅助逆序输出,空间复杂度略高,但是可以避免递归深度过深导致的栈溢出问题。

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/49347.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Golang | Leetcode Golang题解之第268题丢失的数字

题目&#xff1a; 题解&#xff1a; func missingNumber(nums []int) int {n : len(nums)total : n * (n 1) / 2arrSum : 0for _, num : range nums {arrSum num}return total - arrSum }

scrapy爬取城市天气数据

scrapy爬取城市天气数据 一、创建scrapy项目二、修改settings,设置UA,开启管道三、编写爬虫文件四、编写items.py五、在weather.py中导入WeatherSpiderItem类六、管道中存入数据,保存至csv文件七、完整代码一、创建scrapy项目 先来看一下爬取的字段情况: 本次爬取城市天…

【中项】系统集成项目管理工程师-第5章 软件工程-5.3软件设计

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

一文解决 | Linux(Ubuntn)系统安装 | 硬盘挂载 | 用户创建 | 生信分析配置

原文链接&#xff1a;一文解决 | Linux&#xff08;Ubuntn&#xff09;系统安装 | 硬盘挂载 | 用户创建 | 生信分析配置 本期教程 获得本期教程文本文档&#xff0c;在后台回复&#xff1a;20240724。请大家看清楚回复关键词&#xff0c;每天都有很多人回复错误关键词&#xf…

Blackbox AI-跨时代AI产物,你的私人编程助手

1. 引言 随着人工智能技术的飞速发展&#xff0c;我们的生活方式正在经历前所未有的变革。从智能家居到自动驾驶&#xff0c;AI已经渗透到我们生活的方方面面。而在这场科技革命中&#xff0c;Blackbox 网站凭借其先进的技术和全面的功能&#xff0c;成为了众多AI产品中的佼佼者…

不同行情下算法的具体使用!

上一篇我们说到了不同公司算法交易的区分&#xff0c;有朋友提出了不同的行情下的算法交易应该怎么使用&#xff0c;小编今天就带大家了解下&#xff01;当然具体实际状况百出&#xff0c;这种可以实际为准&#xff08;韭菜修养全拼实际探讨交流&#xff09;&#xff01; 我们在…

Redis的使用场景、持久化方式和集群模式

1. Redis的使用场景 热点数据的缓存 热点数据&#xff1a;频繁读取的数据 限时任务的操作。比如短信验证码 完成session共享的问题。因为前后端分离 完成分布式锁 商品的销售量 2. Redis的持久化方式 2.1 什么是持久化 把内存中的数据存储到磁盘的过程。同时也可以把磁盘中…

AI绘画入门实践|Midjourney:使用 --no 去除不想要的物体

在 Midjourney 中&#xff0c;--no 作为反向提示词&#xff0c;告诉 MJ 在生成图像时&#xff0c;不要包含什么。 使用格式&#xff1a;--no 对应物体提示词&#xff08;多个物体之间使用","间隔&#xff09; 使用演示 a web banner, summer holiday --v 6.0 a web b…

【Git多人协作开发】同一分支下的多人协作开发模式

目录 0.前言场景 1.开发者1☞完成准备工作&协作开发 1.1创建dev分支开发 1.2拉取远程dev分支至本地 1.3查看分支情况和分支联系情况 1.4创建本地dev分支且与远程dev分支建立联系 1.5在本地dev分支上开发file.txt 1.6推送push至远程仓库 2.开发者2☞完成准备工作&…

C++第十弹 ---- vector的介绍及使用

目录 前言vector的介绍及使用1. vector的使用1.1 vector的定义1.2 iterator的使用1.3 vector空间增长问题1.4 vector增删查改 2. vector迭代器失效问题(重点) 总结 前言 本文介绍了C中的vector数据结构及其使用方法。 更多好文, 持续关注 ~ 酷酷学!!! 正文开始 vector的介绍…

GPT-4O 的实时语音对话功能在处理多语言客户时有哪些优势?

最强AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量 我瞄了一眼OpenAI春季发布会&#xff0c;这个发布会只有26分钟&#xff0c;你可以说它是一部科幻短片&#xff0c;也可以说它过于“夸夸其谈”&#xff01;关于…

Anconda 快速常用命令简洁版

目的&#xff1a;简单清楚的使用基本的conda 命令 可能需求 查看项目中的虚拟环境及依赖是否满足需求操作新环境来满足项目或者论文的实现 Anconda 常用命令 conda 查看基础命令1. 进入Anaconda 环境2. 查看版本3.查看有哪些虚拟环境4.激活虚拟环境5. 进入虚拟环境查看6. 退出…

如何在网站嵌入可填写的PDF表单:2024巴黎奥运会赛程

如何将可填写的 PDF 表单嵌入您的网页&#xff1f;访问者无需下载或注册即可查看并填写。 简单&#xff01;本文以2024巴黎奥运会赛程表单为例&#xff0c;演示如何将其嵌入网页中。您可以在 ONLYOFFICE 表单库免费获取该模板&#xff0c;有白色和紫色两种背景设计。 如何在网站…

六、抽象工厂模式

文章目录 1 基本介绍2 案例2.1 Drink 抽象类2.2 Tea 类2.3 Coffee 类2.4 DrinkFactory 接口2.5 TeaFactory 类2.6 CoffeeFactory 类2.7 Client 类2.8 Client 类运行结果2.9 总结 3 各角色之间的关系3.1 角色3.1.1 Product ( 抽象产品 )3.1.2 ConcreteProduct ( 具体产品 )3.1.3…

昇思25天学习打卡营第1天|快速入门-实现一个简单的深度学习模型

目录 实验环境 Jupyter云上开发环境使用 导包 处理数据集 网络构建 模型训练 评估模型性能 保存模型 加载模型 预测推理 实验环境 02-快速入门.ipynb (4) - JupyterLab (mindspore.cn) 规格&#xff1a;4u 16G 20G 镜像&#xff1a;py39-ms2.3.0rc1 特性&#xff1…

SpringBoot 最大连接数及最大并发数是多少

SpringBoot 最大连接数及最大并发数 Spring Boot 是一个基于 Spring 框架的快速开发框架&#xff0c;它本身并不直接管理数据库连接或网络连接的最大连接数和最大并发数。这些参数通常由底层的基础设施和组件来控制&#xff0c;例如&#xff1a; 数据库连接池&#xff1a;Spri…

Qt 使用视口和窗口作图

物理坐标系与逻辑坐标系 绘图设备的物理坐标系是基本的坐标系&#xff0c;通过 QPainter 的平移、旋转等坐标变换可以得到更容 易操作的逻辑坐标系。 物理坐标系也称为视口&#xff08;viewport&#xff09;坐标系&#xff0c;逻辑坐标系也称为窗口&#xff08; window&…

netty使用redis发布订阅实现消息推送

netty使用redis发布订阅实现消息推送 场景 项目中需要给用户推送消息: 接口 RestController public class PushApi {Autowiredprivate PushService pushService;/*** 消息推送* param query* return*/PostMapping("/push/message")public String push(RequestBody…

Linux gcc/g++ _ make/makefile

文章目录 库gcc/g程序编译过程链接动态链接静态链接 make _ makefile 库 一、 什么是库&#xff1f; 库是程序代码的集合&#xff0c;是共享程序代码的一种方式。根据源代码的公开情况&#xff0c;库可以分为两种类型&#xff1a; 开源库&#xff0c;公开源代码&#xff0c;能…

Godot入门 03世界构建1.0版

在game场景&#xff0c;删除StaticBody2D节点&#xff0c;添加TileMap节点 添加TileSet图块集 添加TileSet源 拖动图片到图块&#xff0c;自动创建图块 使用橡皮擦擦除。取消橡皮擦后按住Shift创建大型图块。 进入选择模式&#xff0c;TileMap选择绘制&#xff0c;选中图块后在…