【数据结构】带你图文结合深入栈和队列,并具体分步实现

在这里插入图片描述

君兮_的个人主页

勤时当勉励 岁月不待人

C/C++ 游戏开发

Hello,米娜桑们,这里是君兮_,我们继续来学习初阶数据结构的内容,今天我们要讲的是栈与队列部分的内容,这篇博客先讲栈,队列我们放到下次再讲
好了,废话不多说,开始今天的学习吧!

一.栈

1.栈的概念及结构

  • 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
    出栈:栈的删除操作叫做出栈。出数据也在栈顶
    在这里插入图片描述

  • 无论是入栈还是出栈,都遵循后进先出原则。

2.栈的实现方式

  • 栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的不需要付出很大的代价。
    在这里插入图片描述
  • 如果使用链表方式实现的话,会出现一个问题,我们的栈实际上是通过尾插实现入栈的,也就是说在入栈时我们每一次都需要通过遍历的方式找尾,或者使用双头循环链表的方式找尾,但是由于数组的连续性和支持随机访问,对比于链表无疑是更加方便的。因此,接下来内容中的栈结构我们就通过数组的方式实现。

3.栈的分类

  • 栈分为静态栈和动态栈

静态栈

// 下面是定长的静态栈的结构,实际中一般不实用
typedef int STDataType;
#define N 10
typedef struct Stack
{
STDataType a[N];
int _top; // 栈顶
int _capacity; // 容量
}Stack;

动态栈

  • 动态栈是通过动态内存开辟的方式实现的,由于支持动态内存的变化,因此在实际应用中动态栈无疑是更好的选择
typedef int STDataType;//方便以后修改栈中存储的数据类型
typedef struct Stack
{
    STDataType* a;//数组
    int top;//存放栈中有效元素的个数
    int capacity;//栈的容量大小
}Stack;
// 初始化栈
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
//获取栈底元素
STDataType StackTail(Stack* ps);

4.动态栈的实现

栈的初始化函数 StackInit

  • 无论是什么数据结构,我们都得先初始化
// 初始化栈
void StackInit(Stack* ps)
{
    assert(ps);//断言
    ps->a = NULL;//数组中还没有元素
    //容量和有效元素都为0
    ps->capacity = 0;
    ps->top = 0;
}

入栈函数 StackPush

// 入栈
void StackPush(Stack* ps, STDataType data)
{
    assert(ps);
    //判断是否还有空间
    if (ps->capacity == ps->top)
    {
        int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//通过容量判断此时栈中有没有元素,如果没有元素就申请4个字节的空间,如果有容量,但容量不够时就把容量变为之前的2倍
        STDataType* tmp = (STDataType*)realloc(ps->a, newCapacity* sizeof(STDataType));
        if (tmp == NULL)//判读开辟空间是否成功
        {
            perror("realloc failed");
            exit(-1);
        }
        ps->a = tmp;//成功了就把这个新的空间给我们的a数组
        ps->capacity = newCapacity;//容量也要做相应的动态变化
    }
    ps->a[ps->top] = data;//往栈中存元素
    ps->top++;//有效元素++
}

在这里插入图片描述

  • 这里可能有些人对于动态内存管理的内容不够了解,可以看看我这篇博客:【C语言进阶】那些你必须掌握的C/C++要点——动态内存管理(1)
  • 这里还有一点要提,这里为什么用realloc开辟空间而不是使用malloc呢?
  • 这里就涉及一些realloc的进阶玩法
  • 我们知道realloc是用来修改动态开辟的内存大小,但是当我们给它传入的是一个空指针,它的功能与malloc就是相同的。

出栈的函数 StackPop

// 出栈
void StackPop(Stack* ps)
{
    assert(ps);
    --ps->top;
}
  • 出栈就非常简单啦,我们直接让top减1就行,由于我们的数组是通过下标的方式访问数组成员的,top只有减少就无法在找到相应的元素啦
    在这里插入图片描述

获取栈顶元素的函数 StackTop

// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    //有效元素为top,数组的下标得-1
    return ps->a[ps->top-1];
}
  • 我们栈中的元素是尾插的,因此最后一个元素就是我们的栈顶元素

获取栈底元素 StackTail

  • 需要栈底的情况其实并不常见,我们会在某些特别情况下才会用到(比如我们之后会带大家写的oj题)
STDataType StackTail(Stack* ps)
{
   assert(ps);
   return ps->a[0];
}
  • 我们的元素是尾插进数组的,因此我们的首元素就是我们的栈底元素。

获取栈中有效元素的个数的函数 StackSize

// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
    assert(ps);
    return ps->top;
}
  • 我们在之前无论是入栈还是出栈都变化了我们的有效元素top,因此返回的top就是我们的有效元素个数。

判断栈是否为空的函数 StackEmpty

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps)
{
    assert(ps);
    return ps->top == 0;
}
  • top存着我们的有效元素,因此可以通过判断top是否为0来判断栈是否为空

销毁栈的函数

// 销毁栈
void StackDestroy(Stack* ps)
{
    assert(ps);
    free(ps->a);//free掉动态开辟的数组
    ps->a = NULL;//置空
    ps->top = ps->capacity = 0;//把有效元素和容量全部清空
}
  • 我们的内存是通过动态开辟出来的,因此当我们使用完后就必须把我们的申请的内存给free掉防止内存泄漏。

5.测试栈

  • 我们来试试的效果
void TestStack1()
{
    Stack ST;
    StackInit(&ST);
    StackPush(&ST, 10);
    StackPush(&ST, 20);
    StackPush(&ST, 30);
    while (!StackEmpty(&ST))
    {
        printf("%d ", StackTop(&ST));
        StackPop(&ST);
}
    
    StackDestroy(&ST);
   

}
int main()
{
    
    TestStack1();
    return 0;
}

在这里插入图片描述

总结

  • 今天的内容到这里就结束了,如果你能理解之前讲过的顺序表,链表等,栈的内容其实非常的简单,想学好的话,一定要自己动手试试哦!!
  • 好了,如果你有任何疑问欢迎在评论区或者私信我提出,大家下次再见啦!

新人博主创作不易,如果感觉文章内容对你有所帮助的话不妨三连一下这个新人博主再走呗。你们的支持就是我更新的动力!!!

**(可莉请求你们三连支持一下博主!!!点击下方评论点赞收藏帮帮可莉吧)**

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PY32F003 FLASH

了解py32芯片的flash内容,对于py32进行api升级有更好的了解的操作 //uiOffset 0(4MHz), 1(8MHz), 2(16MHz), 3(22.12MHz), 4(24MHz) void SetFlashParameter(uint32_t uiOffset) {WRITE_REG(FLASH->KEYR, FLASH_KEY1);WRITE_REG(FLASH->KEYR, FLASH_KEY2); …

解决Error running XXXApplicationCommand line is too long.报错

测试IDEA版本:2019.2.4 ,2020.1.3 文章目录 一. 问题场景二. 报错原因2.1 为什么命令行过长会导致这种问题? 三. 解决方案3.1 方案一3.2 方案二 一. 问题场景 当我们从GitHub或公司自己搭建的git仓库上拉取项目代码时,会出现以下错误 报错代…

PHP8的循环控制语句-PHP8知识详解

我们在上一节讲的是条件控制语句,本节课程我们讲解循环控制语句。循环控制语句中,主要有for循环、while循环、do...while循环和foreach循环。 在编写代码时,经常需要反复运行同一代码块。我们可以使用循环来执行这样的任务,而不是…

GWJDN-400型2MHZ自动平衡高温介电温谱仪

GWJDN-400型2MHZ自动平衡高温介电温谱仪 GWJDN-400型2MHZ自动平衡高温介电温谱仪 关键词:介电常数,高温介电,自动平衡 主要功能: 材料介电常数测试仪 半导体材料的介电常数、导电率和C-V特性液晶材料:液晶单元的介电常数、弹性…

新能源汽车交流充电桩控制主板的功能维度

新能源汽车交流充电桩控制主板的功能维度 交流充电桩主板是电动汽车充电站的关键组件,它负责控制充电过程,保护设备和电网免受电动汽车充电的冲击。它具有控制、保护、检测、报警和记录等功能,可以有效地控制充电过程,保证交流充电…

dueling network原理和实现

算法原理: Q ( s , a ; θ , α , β ) V ( s ; θ , β ) ( A ( s , a ; θ , α ) − max ⁡ a ′ ∈ ∣ A ∣ A ( s , a ′ ; θ , α ) ) . \begin{gathered}Q(s,a;\theta,\alpha,\beta)V(s;\theta,\beta)\left(A(s,a;\theta,\alpha)-\max_{a\in|\mathcal{A}…

文件或目录损坏且无法读取

如上图报错,我们直接用cmd命令输入【CHKDSK C: /F】然后回车 电脑重启后可以了,希望能帮助各位小伙伴

知识付费系统开发:构建高效智能的付费内容平台

随着数字化时代的来临,知识付费正迅速崭露头角,为知识创作者和求知者带来了全新的商机。在这个背景下,开发一款高效智能的知识付费系统成为了一项重要的任务。本文将深入探讨如何基于Python编程语言和相关技术构建一个智能的知识付费内容平台…

Excel表格(一)

1.单一栏的宽度和高度设置 2.大标题的跨栏居中 3.让单元格内的文字------自动适应 4.序号递增 5.货币符号 6.日期格式的选择 选到单元格,选中对应的日期格式 7.自动求和的计算 然后在按住回车键即可求出当前行的金额 点击自动求和 8.冻结表格栏 9.排序 1.单栏排序 …

python接口自动化之自动发送测试报告邮件

前言 ​ SMTP(Simple Mail Transfer Protocol)也就是简单邮件传输协议,是一种提供可靠且有效电子邮件传输的协议。python的smtplib模块就提供了一种很方便的途径发送电子邮件,它对smtp协议进行了简单的封装。 ​ python发邮件主…

【数据结构】“单链表”的练习题

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …

【云原生】kubectl命令的详解

目录 一、陈述式资源管理方式1.1基本查看命令查看版本信息查看资源对象简写查看集群信息配置kubectl自动补全node节点查看日志 1.3基本信息查看查看 master 节点状态查看命名空间查看default命名空间的所有资源创建命名空间app删除命名空间app在命名空间kube-public 创建副本控…

Zebec Protocol 将进军尼泊尔市场,通过 Zebec Card 推动该地区金融平等

流支付正在成为一种全新的支付形态,Zebec Protocol 作为流支付的主要推崇者,正在积极的推动该支付方案向更广泛的应用场景拓展。目前,Zebec Protocol 成功的将流支付应用在薪酬支付领域,并通过收购 WageLink 将其纳入旗下&#xf…

clickhouse断电重启故障解决方案

业务场景 公司的一个日志系统用到了clickhouse。一线运维反映说有个生产环境因为异常断电造成服务器重启。在执行日志系统的启动脚本时,一直报clickhouse启动不起来,日志系统无法使用。 问题排查 通过阅读启动脚本代码,以及启动日志系统&a…

比特鹏哥5-数组【自用笔记】

比特鹏哥5-数组【自用笔记】 1.数组的概念2.一维数组的创建和初始化创建的语句结构初始化的语句结构 3.一维数组的使用数组的下标:从0开始,n个数组,最后一个的下标是n-1 4.一维数组在内存中的存储5.sizeof计算数组元素个数可以计算元素个数并…

农业大数据可视化平台,让农业数据更直观展现!

农业大数据可视化平台是指利用大数据技术和可视化工具,对农业领域的数据进行收集、整理、分析和展示的平台。它可以帮助农业从业者更好地理解和利用农业数据,提高农业生产效率和决策水平。 农业大数据可视化平台通常具有以下特点和功能: 数据…

利用Arthas+APM监控进行Java性能深度定位

大家可能都用过APM监控,包括开源的Skywalking、商用的卓豪(ZOHO)ManageEngine APM应用性能监控、以及云监控产品如听云(Server监控),这些APM监控产品大大方便了我们实时监控应用性能,并实现性能…

Unity限制在一个范围内移动

Unity限制在一个范围内移动 这个例子中,我们学习Vector3.ClampMagnitude的用法,限制小球在范围内移动。 在地图上放了一个小球,让他移动,但是不想让他掉下去,限制在一个球星范围内,就好像绳子拴住了一样&…

论文阅读---《Unsupervised ECG Analysis: A Review》

题目 无监督心电图分析一综述 摘要 电心图(ECG)是检测异常心脏状况的黄金标准技术。自动检测心电图异常有助于临床医生分析心脏监护仪每天产生的大量数据。由于用于训练监督式机器学习模型的带有心脏病专家标签的异常心电图样本数量有限,对…

混合云环境实现K8S可观测的6大策略

2023年,原生云应用及平台发展迅猛。大量企业都在努力发挥其应用程序的最大潜力,以确保极致的用户体验并推动业务增长。 混合云环境的兴起和容器化技术(如Kubernetes)的采用彻底改变了现代应用程序的开发、部署和扩展方式。 在这个数字舞台上,…
最新文章