当前位置: 首页 > news >正文

数据结构-栈(理解版)

一、栈的定义

相信大家对于栈或多或少有一些了解,可能大多数人会告诉你栈是一种先进后出的数据结构。这其实说了跟没说一样(❁´◡`❁)!当然(last in,first out)是栈最有特色的性质。

这里可以给大家一些比较好理解的例子,像男生对枪械感兴趣的,不难发现:弹夹其实就是一个先进后出的容器,压入子弹后,先打出去的一定是最后压入的。同理,第一枚压入的子弹一定是最后打出的。

再比如,我们在浏览网页时,经常会有回退(即回到上一个浏览的网页)这一操作,那么同样不难想象,第一次回退操作的结果肯定是回到,除当前页面最后浏览的,而并不是第一个浏览的网页,这同样是先进后出的典型代表。

接下来我们展开讲一讲怎么将后进先出抽象出来。这里我们先回顾一下线性表的插入和删除操作,先找到插入位置的前驱结点,然后做指针或数组下标的相关操作。那么栈只不过是将操作对象局限为最后一个结点罢了。

那么其实总结起来说,栈还就只是一种后进先出的容器罢了,更简单明了地说,栈的本质还是一种顺序表,不过是只能在其一端进行插入和删除操作的特殊线性表

那么就会有很多人问,既然栈的本质还是一种线性表,那我们为什么还要去重新定义这样一种数据结构呢?直接在线性表的基础上进行数组下标或者是链表指针的操作不就行了。首先我想说这是一个好问题,因为我也有这样的疑问。但是大家想一下,我们既然有了数组,为什么还要有顺序表呢?因为大家清楚顺序表本质就是一个数组。其实这就涉及到程序设计的问题,我们当然希望在编写程序的时候关注的问题越少越好,所以栈这个数据结构的创建就使得程序设计时的思考范围变小了,可以不需要去花精力考虑类似于数组下标等问题。

二、理解并手撕栈

头文件及宏定义

#include <stdio.h>
#include <stdlib.h>#define elemtype int
#define initcapacity 10
#define increment 10

initcapacity:初始化栈时的最大容量。

increment:在后面栈满后对空间进行再分配时,栈的最大空间的增量。

栈的定义

(这里以顺序栈为例,后期会更新其他实现:链栈、队列栈)

typedef struct Stack{elemtype *base;elemtype *top;int stacksize;
} Stack;

结构体中定义了两个elemtype类型的指针(*top*base),然后是栈的最大容量stacksize。其中*top指向栈顶,*base指向栈底,所以由栈的LIFO特性可以知道,所有的操作都是在top一端进行的。

初始化栈(initStack)

Stack* initStack(){Stack *s=(Stack*)malloc(sizeof(Stack));s->base=(elemtype*)malloc(sizeof(elemtype)*initcapacity);s->top=s->base;s->stacksize=initcapacity;return s;
}

初始化时,当然是先要定义结构体Stack *s,然后将结构体中的元素(*top*base)分配空间,而空间的长度就是宏定义的initcapacity最后还要更新一下s->stacksize

(ps:此处可以给s->base分配,再将其赋给s->top,也可以反过来,没有区别)

(ps:函数返回值是*Stack,当然也可以传入*Stack指针然后再进行初始化。)

销毁栈(destroyStack)

void destroyStack(Stack *s){  free(s->base);            s->base=NULL;      s->top=NULL;s->stacksize=0;          
}   

这里进行的操作是,先对结构体中的s->base进行free()操作,然后将其指针指置空。最后更新s->stacksize=0

这里大家可能也会有问题,为什么只free(s->base),不用free(s->top)

这里要简单解释一下free()函数的机制,我们输入由动态分配的内存的首地址,然后函数会将自定计算这块内存的长度,然后将其设置为可分配状态,最后由操作系统释放掉。而我们这里的首地址是s->base,s->top并不是首地址,所以应该释放掉s->base

这里可能大家还会有疑惑,既然是将栈销毁,为什么不直接free(s),也就是直接将结构体释放掉,这样难道不是更方便吗?

首先我们要明确销毁栈的目的是什么?应该是,在我不需要这个栈时,我希望这一块空间被释放掉,也就是可被再分配

那么如果直接将释放结构体,那么在结构体中动态分配的内存(也就是s->base)是无法释放的,这也就会导致内存泄漏,与我们的目的就不符了。

清空栈(clearStack)

void clearStack(Stack *s){s->top=s->base;s->stacksize=0; 
} 

清空栈比较简单,我只需要将将top指针重置为base就可以了。

(ps:这种方式的清空并不是真正意义上的将栈中元素删除了,它其实是一种清空的假象,栈的物理结构并没有改变,只不过那些没有删去的元素不用再去访问它了,在我要入栈时就会覆盖掉那些元素

!!!如果有强迫症的话,可以将每一个元素删除 (^///^)。

压栈(pushStack)

void pushStack(Stack *s,elemtype e){if(s->top - s->base >= s->stacksize){s->base=(elemtype*)realloc(s->base,sizeof(elemtype)*(initcapacity+increment));if(!s->base){printf("fail realloc\n");exit(0);}s->top=s->base+s->stacksize;s->stacksize+=increment;}*s->top=e;s->top++;
}

其实压栈的核心操作就是两行代码:

*s->top=e;

 s->top++;

 另一大坨主要是考虑扩容代码健壮性的校验代码:

s->base=(elemtype*)realloc(s->base,sizeof(elemtype)*(initcapacity+increment));

值得注意的是如果不熟悉realloc()函数,要注意一下该函数的输入,两个参数,(原内存首地址,再分配后的内存空间总数) ,然后进行强制类型转换。

弹栈(popStack)

void popStack(Stack *s){if(s->top==s->base){printf("stack is empty\n");exit(0);}printf("pop-%d\n",*(--s->top));
} 

弹栈的操作比较简单,先判断是否为空栈,然后将顶部元素弹出。这里需要注意的是s->top并不是头部元素,一般情况下它是空的,等待着要入栈的元素。所以弹出的元素应该是,--s->top指向的元素。

(ps:s->top是一个指针,要输出值的话,是*s->top

栈长度、顶部元素获取

int lengthStack(Stack *s){return s->top - s->base;
}elemtype GetTop(Stack *s){return *(s->top-1);   //时刻记住s->top,是一个指针,要取值的话,加*!!!
}

这里涉及到的知识带是,指针可以相减获得两指针的距离

s->top - s->base;

同样,这里需要注意栈顶元素是*(s->top-1)

遍历栈(traverseStack)

void traveraeStack(Stack *s){if(s->base==s->top){printf("Stack is empty!\n");exit(0);}int len=lengthStack(s);for(int i=1;i<=len;++i){printf("%d->",*(s->top-i));  }                                printf("end\n");                 
}                                    

这里在遍历的时候也有一个小细节,不能直接写 *--(s->top),因为*s是作为指针传入的,这样会修改结构体中s->top的值,那么遍历结束后s->top就会与s->base重合了!!!此时再进行遍历就会是空栈!!!

运行结果

没有bug!!! 

三、写在最后

其实栈的实现逻辑是很简单的,就抓住栈的LIFO特性就可以了,后面的学的队列也是这样。

今天是以顺序栈为例分析的,其实还有链表实现栈,队列实现栈,以及以栈为基础的双端栈,这些数据结构都会之后的博客中,我都会写到。然后文章有什么问题的话,希望大家能够帮忙指正!

然后源代码已经上传到下面了,有需要的可以自取。

顺序栈(源码)

相关文章:

数据结构-栈(理解版)

一、栈的定义 相信大家对于栈或多或少有一些了解&#xff0c;可能大多数人会告诉你栈是一种先进后出的数据结构。这其实说了跟没说一样(❁◡❁)&#xff01;当然&#xff08;last in&#xff0c;first out&#xff09;是栈最有特色的性质。 这里可以给大家一些比较好理解的例…...

NAND Flash虚拟层初始化

在整个NAND Flash初始化过程中,其主要过程由NAND_Init()函数来完成的,因此以下以NAND_Init()函数作为入口,对NAND Flash虚拟层初始化进行全面分析: NAND_Init()NAND_PhyInit()FMT_Init()FMT_FormatNand()LML_Init() NAND_Init()函数首先调用NAND_PhyInit()函数…...

zabbix7.0监控linux主机案例详解

前言 服务端配置 链接: rocky9.2部署zabbix服务端的详细过程 环境 主机ip应用zabbix-server192.168.10.11zabbix本体zabbix-client192.168.10.12zabbix-agent zabbix-server(服务端已配置) 具体实现过程 zabbix-client配置 安装zabbix-agent 添加扩展包 dnf -y instal…...

2024重生之回溯数据结构与算法系列学习(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】

欢迎各位彦祖与热巴畅游本人专栏与博客 你的三连是我最大的动力 以下图片仅代表专栏特色 专栏跑道一 ➡️ MYSQL REDIS Advance operation 专栏跑道二➡️ 24 Network Security -LJS ​ ​ ​ 专栏跑道三 ➡️HCIP&#xff1b;H3C-SE;CCIP——LJS[华为、华三、思科高级网络]…...

django drf 过滤器

排序 代码&#xff1a; from rest_framework.generics import ListAPIView from rest_framework.filters import OrderingFilterclass TestListAPIView(ListAPIView):queryset models.Course.objects.filter(is_deleteFalse).all()serializer_class serializers.TestModelS…...

蓝桥杯—STM32G431RBT6(RTC时钟获取时间和日期)

一、RTC是什么&#xff0c;有什么用&#xff1f; 在 STM32 中&#xff0c;RTC&#xff08;Real-Time Clock&#xff0c;实时时钟&#xff09;主要有以下作用&#xff1a; 时间保持&#xff1a;即使在系统断电情况下&#xff0c;也能持续记录时间。&#xff08;需要纽扣电池供电…...

DriveVLM 论文学习

论文链接&#xff1a;https://arxiv.org/abs/2402.12289 解决了什么问题&#xff1f; 自动驾驶对交通行业有着革命性的作用&#xff0c;实现 FSD 的一个主要障碍就是场景理解。场景理解涉及在复杂且不可预测的环境中进行导航&#xff0c;这些环境可能包括恶劣的天气条件、复杂…...

Unity3D 客户端多开

Unity3D 实现客户端多开 客户端多开 最近在做好友聊天系统&#xff0c;为了方便测试&#xff0c;需要再开一个客户端。 简单的方法&#xff0c;就是直接拷贝一个新的项目&#xff0c;但是需要很多时间和占用空间。 查阅了网络资料&#xff0c;发现有一种软链接&#xff0c;…...

使用代理IP数据采集都需要注意那些?

“在当今大数据时代&#xff0c;数据采集成为了企业决策和个人研究的重要依据。然而频繁访问目标网站往往会引发IP被封锁的风险&#xff0c;这时使用代理IP就显得尤为重要。但代理IP的使用并非毫无风险&#xff0c;以下是使用代理IP进行数据采集时需要注意的几个关键事项。” 一…...

城市大脑:智慧城市的神经中枢——典型实践与经验启示

随着信息技术的飞速发展&#xff0c;智慧城市已成为全球城市转型升级的重要方向。“城市大脑”作为智慧城市的核心引擎&#xff0c;正以其强大的数据处理能力、智能决策支持和跨领域协同优势&#xff0c;引领着城市管理与服务的深刻变革。本文将深入探讨几个具有代表性的“城市…...

嵌入式中CW32多功能测试笔实现

前言 起心动念 在日常的硬件调试工作中,我们最常使用的仪器仪表可能就是万用表了,虽然万用表号称“万用”,但大部分时候,我们需要使用到的功能无非是电压测量和通断测量。 作为调试的“得力干将”,万用表有时候也会存在存在一些缺点和局限性,比如:体积较大不便于携带…...

Python 时间占位符:毫秒的使用

Python 时间占位符&#xff1a;毫秒的使用 在 Python 中&#xff0c;处理时间和日期是一个非常常见的任务。在进行时间格式化时&#xff0c;使用占位符来表示特定的时间单位是非常重要的。特别是毫秒&#xff08;ms&#xff09;&#xff0c;它在许多应用中扮演着关键角色&…...

深度学习:(七)梯度下降法在神经网络中的应用

梯度下降法在神经网络中的应用 事先规定&#xff1a; 用 n n n 表示个数&#xff08;维度&#xff09;: n [ 0 ] n x n^{[0]}n_x n[0]nx​ &#xff0c;表示单个训练样本 x x x 的元素个数&#xff1b; n [ 1 ] n^{[1]} n[1] 表示隐藏层 1 1 1 的单元&#xff08;节点&am…...

HarmonyOS---权限和http/Axios网络请求

网络请求(http,axios) 目录 一、应用权限管理1.1权限的等级1.2授权方式1.3声明权限的配置1.4如何向用户进行申请 二、内置http请求使用三、Axios请求使用&#xff08;建议&#xff09;3.1 使用方式一3.2 使用方式二&#xff08;建议&#xff09; 一、应用权限管理 应用权限保护…...

信号量SEM

前提 1.信号量的本质是一把计数器 2.申请信号本质就是预订资源 3.PV操作是原子的! 将一个公共资源当做整体访问-->锁 如果公共资源不当做整体使用&#xff0c;多进程可以并发的访问公共资源&#xff0c;但不是同一个区域&#xff0c;为了将资源均分&#xff0c;所以有了…...

828华为云征文 | 基于华为云Flexus云服务器X搭建部署——AI知识库问答系统(使用1panel面板安装)

&#x1f680;对于企业来讲为什么需要华为云Flexus X来搭建自己的知识库问答系统&#xff1f;&#xff1f;&#xff1f; 【重塑知识边界&#xff0c;华为云Flexus云服务器X引领开源问答新纪元&#xff01;】 &#x1f31f; 解锁知识新动力&#xff0c;华为云Flexus云服务器X携…...

从零预训练一个tiny-llama#Datawhale组队学习Task2

完整的教程请参考&#xff1a;datawhalechina/tiny-universe: 《大模型白盒子构建指南》&#xff1a;一个全手搓的Tiny-Universe (github.com) 这是Task2的学习任务 目录 Qwen-blog Tokenizer&#xff08;分词器&#xff09; Embedding&#xff08;嵌入&#xff09; RMS …...

【Linux探索学习】第二弹——Linux的基础指令(中)——夯实基础第二篇

Linux基础指令&#xff08;上&#xff09;&#xff1a;【Linux探索学习】第一弹——Linux的基本指令&#xff08;上&#xff09;——开启Linux学习第一篇-CSDN博客 前言&#xff1a; 在前面我们已经讲解了一些常用的Linux的基础指令&#xff0c;那些当然是远远不够的&#xff…...

Python和QT哪个更适合嵌入式方向的上位机开发?

最近因为工作需要&#xff0c;需要做一个上位机用来处理收集到的数据&#xff0c;然后进行分析&#xff0c;最好有图标输出&#xff0c;当然还要考虑开发便捷&#xff0c;毕竟平时主要是嵌入式方向开发&#xff0c;核心技术栈主要是Linux和C语言&#xff0c;对于开始上位机并不…...

Unity实战案例全解析:RTS游戏的框选和阵型功能(5)阵型功能 优化

前篇&#xff1a;Unity实战案例全解析&#xff1a;RTS游戏的框选和阵型功能&#xff08;4&#xff09;阵型功能-CSDN博客 本案例来源于unity唐老狮&#xff0c;有兴趣的小伙伴可以去泰克在线观看该课程 我只是对重要功能进行分析和做出笔记分享&#xff0c;并未无师自通&#x…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)

UniApp 集成腾讯云 IM 富媒体消息全攻略&#xff08;地理位置/文件&#xff09; 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型&#xff0c;核心实现方式&#xff1a; 标准消息类型&#xff1a;直接使用 SDK 内置类型&#xff08;文件、图片等&#xff09;自…...