数据结构-栈(理解版)
一、栈的定义
相信大家对于栈或多或少有一些了解,可能大多数人会告诉你栈是一种先进后出的数据结构。这其实说了跟没说一样(❁´◡`❁)!当然(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特性就可以了,后面的学的队列也是这样。
今天是以顺序栈为例分析的,其实还有链表实现栈,队列实现栈,以及以栈为基础的双端栈,这些数据结构都会之后的博客中,我都会写到。然后文章有什么问题的话,希望大家能够帮忙指正!
然后源代码已经上传到下面了,有需要的可以自取。
顺序栈(源码)
相关文章:
数据结构-栈(理解版)
一、栈的定义 相信大家对于栈或多或少有一些了解,可能大多数人会告诉你栈是一种先进后出的数据结构。这其实说了跟没说一样(❁◡❁)!当然(last in,first out)是栈最有特色的性质。 这里可以给大家一些比较好理解的例…...
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;H3C-SE;CCIP——LJS[华为、华三、思科高级网络]…...
django drf 过滤器
排序 代码: 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是什么,有什么用? 在 STM32 中,RTC(Real-Time Clock,实时时钟)主要有以下作用: 时间保持:即使在系统断电情况下,也能持续记录时间。(需要纽扣电池供电…...
DriveVLM 论文学习
论文链接:https://arxiv.org/abs/2402.12289 解决了什么问题? 自动驾驶对交通行业有着革命性的作用,实现 FSD 的一个主要障碍就是场景理解。场景理解涉及在复杂且不可预测的环境中进行导航,这些环境可能包括恶劣的天气条件、复杂…...
Unity3D 客户端多开
Unity3D 实现客户端多开 客户端多开 最近在做好友聊天系统,为了方便测试,需要再开一个客户端。 简单的方法,就是直接拷贝一个新的项目,但是需要很多时间和占用空间。 查阅了网络资料,发现有一种软链接,…...
使用代理IP数据采集都需要注意那些?
“在当今大数据时代,数据采集成为了企业决策和个人研究的重要依据。然而频繁访问目标网站往往会引发IP被封锁的风险,这时使用代理IP就显得尤为重要。但代理IP的使用并非毫无风险,以下是使用代理IP进行数据采集时需要注意的几个关键事项。” 一…...
城市大脑:智慧城市的神经中枢——典型实践与经验启示
随着信息技术的飞速发展,智慧城市已成为全球城市转型升级的重要方向。“城市大脑”作为智慧城市的核心引擎,正以其强大的数据处理能力、智能决策支持和跨领域协同优势,引领着城市管理与服务的深刻变革。本文将深入探讨几个具有代表性的“城市…...
嵌入式中CW32多功能测试笔实现
前言 起心动念 在日常的硬件调试工作中,我们最常使用的仪器仪表可能就是万用表了,虽然万用表号称“万用”,但大部分时候,我们需要使用到的功能无非是电压测量和通断测量。 作为调试的“得力干将”,万用表有时候也会存在存在一些缺点和局限性,比如:体积较大不便于携带…...
Python 时间占位符:毫秒的使用
Python 时间占位符:毫秒的使用 在 Python 中,处理时间和日期是一个非常常见的任务。在进行时间格式化时,使用占位符来表示特定的时间单位是非常重要的。特别是毫秒(ms),它在许多应用中扮演着关键角色&…...
深度学习:(七)梯度下降法在神经网络中的应用
梯度下降法在神经网络中的应用 事先规定: 用 n n n 表示个数(维度): n [ 0 ] n x n^{[0]}n_x n[0]nx ,表示单个训练样本 x x x 的元素个数; n [ 1 ] n^{[1]} n[1] 表示隐藏层 1 1 1 的单元(节点&am…...
HarmonyOS---权限和http/Axios网络请求
网络请求(http,axios) 目录 一、应用权限管理1.1权限的等级1.2授权方式1.3声明权限的配置1.4如何向用户进行申请 二、内置http请求使用三、Axios请求使用(建议)3.1 使用方式一3.2 使用方式二(建议) 一、应用权限管理 应用权限保护…...
信号量SEM
前提 1.信号量的本质是一把计数器 2.申请信号本质就是预订资源 3.PV操作是原子的! 将一个公共资源当做整体访问-->锁 如果公共资源不当做整体使用,多进程可以并发的访问公共资源,但不是同一个区域,为了将资源均分,所以有了…...
828华为云征文 | 基于华为云Flexus云服务器X搭建部署——AI知识库问答系统(使用1panel面板安装)
🚀对于企业来讲为什么需要华为云Flexus X来搭建自己的知识库问答系统??? 【重塑知识边界,华为云Flexus云服务器X引领开源问答新纪元!】 🌟 解锁知识新动力,华为云Flexus云服务器X携…...
从零预训练一个tiny-llama#Datawhale组队学习Task2
完整的教程请参考:datawhalechina/tiny-universe: 《大模型白盒子构建指南》:一个全手搓的Tiny-Universe (github.com) 这是Task2的学习任务 目录 Qwen-blog Tokenizer(分词器) Embedding(嵌入) RMS …...
【Linux探索学习】第二弹——Linux的基础指令(中)——夯实基础第二篇
Linux基础指令(上):【Linux探索学习】第一弹——Linux的基本指令(上)——开启Linux学习第一篇-CSDN博客 前言: 在前面我们已经讲解了一些常用的Linux的基础指令,那些当然是远远不够的ÿ…...
Python和QT哪个更适合嵌入式方向的上位机开发?
最近因为工作需要,需要做一个上位机用来处理收集到的数据,然后进行分析,最好有图标输出,当然还要考虑开发便捷,毕竟平时主要是嵌入式方向开发,核心技术栈主要是Linux和C语言,对于开始上位机并不…...
Unity实战案例全解析:RTS游戏的框选和阵型功能(5)阵型功能 优化
前篇:Unity实战案例全解析:RTS游戏的框选和阵型功能(4)阵型功能-CSDN博客 本案例来源于unity唐老狮,有兴趣的小伙伴可以去泰克在线观看该课程 我只是对重要功能进行分析和做出笔记分享,并未无师自通&#x…...
Android compose 的基本环境搭建
1.创建项目 导入版本 1.gradle/libs.versions.toml [versions] accompanistPermissions "0.36.0" agp "8.5.0-beta01" coilCompose "2.7.0" constraintlayoutComposeVersion "1.0.1" hiltAndroid "2.51.1" hiltNavi…...
git | 合并 commit 的两种方法
比如你最近的 3 次提交分别为 A B C,你想将它们合并成 X。 方案一 使用 git rebase -i HEAD~3 进入编辑: pick 0148079 A pick 29cae72 B pick bf8572a C修改: r 0148079 A f 29cae72 B f bf8572a C:wq 保存进入 commit 编辑页面,输入 X …...
Grafana链接iframe嵌入Web前端一直跳登录页面的问题记录
概述 公司有个项目使用到Grafana作为监控界面,因为项目方的环境极其复杂,仅物理隔离的环境就有三四个,而且每个都得部署项目,今天在某个环境测试,查看界面遇到一个比较奇怪的Grafana问题,后面针对该问题进行跟踪分析并解决,故而博文记录,用于备忘。 问题 登录项目We…...
后端Java-SpringBoot整合MyBatisPlus步骤(超详细)
1.新建项目。 2.点击完上一步的next之后,选择pom.xml文件中的依赖。 3.点击pom文件进行项目初始化。 按照下面的俩步骤刷新一下maven ,让文件生效 4.新建一个application.yml文件 5. 新建一个数据库mp,在数据库中新建一张user表 6.连接数据…...
8609 哈夫曼树
### 思路 1. **选择最小权值节点**:在哈夫曼树构建过程中,选择两个权值最小且父节点为0的节点。 2. **构建哈夫曼树**:根据权值构建哈夫曼树,确保左子树权值小于右子树权值。 3. **生成哈夫曼编码**:从叶子节点到根节点…...
docker的harbor仓库登录问题
目录 一、问题描述 二、证书信任问题 三、DNS解析问题 四、解决 参考链接:Docker login Harbor报错解决:Error response from daemon: Get https:..-阿里云开发者社区 一、问题描述 问题: 挂机或者挂机重启之后harbor登录不上 查看日…...
ENV | docker 安装使用(简单实操版)
1. 详细步骤 1.1 安装 sudo apt update sudo apt install docker.io1.2 验证(可跳过) docker -v1.3 使用 1.3.1 拉取镜像 # 镜像源,如使用腾讯云服务器,可使用 https://mirror.ccs.tencentyun.com docker pull xxx1.3.2 运行…...
【Golang】深入解读Go语言中的错误(error)与异常(panic)
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
DMDSC更换DCR和VOTE磁盘
DMDSC更换DCR和VOTE磁盘 为了提高DMDSC集群运行速度和节点之间通信协调的效率,需要将运行在机械盘上的dcr和vote磁盘替换到SSD高效磁盘上。将原来200M的dcr和vote机械磁盘,换成500M的SSD高效磁盘。 磁盘替换规划信息如下所示: 信息说明 替…...
国产化框架PaddleYOLO结合Swanlab进行作物检测
1. 项目介绍 粮食安全,作为人类生存与发展的基石,始终是全球关注的焦点。它不仅仅关乎粮食的充足供应,更涉及粮食的质量安全、营养健康以及可持续生产等多个维度。在全球化、气候变化和资源环境约束日益加剧的背景下,如何确保粮食…...
做网站编辑好吗/百度应用商店app下载安装
如果你感觉累,那就对了那是因为你在走上坡路。。这句话似乎有点道理的样子,时常提醒自己无论走到哪都不要忘记自己当初为什么出发。有时想想感觉有的东西可以记录一下,就把它记录下来吧,这次想写一下关于单张图片点击全屏预览的问…...
共享互助医疗网站建设/西安楼市最新房价
什么是mock接口呢,举个栗子,你在一家电商公司,有查看商品、购物、支付、发 货、收获等等等一大堆功能,你是一个测试人员,测测测,测到支付功能的时候,你就要调用第三方支付接口了,真实…...
一级a做爰视频安全网站/网络推广外包要多少钱
ALLEXCEPT函数ALLEXCEPT函数属于“筛选”类函数,隶属于“表函数”,在ALL函数系列家族中,其地位是不可或缺的。EXCEPT翻译成中文是什么意思?表示:除了的意思。因此,这个函数所表达的意思顾名思义,…...
网站企业建站/seo1域名查询
还是畅通project Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 26860 Accepted Submission(s): 11985Problem Description某省调查乡村交通状况,得到的统计表中列出了随意两村庄间的距离。省政府“…...
设计中国第一架飞机/武汉seo外包平台
GPIO库函数介绍 重要函数 //1个初始化函数 void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); //2个读取输入电平函数 uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 作用:读取某个GPIO的输入电平,实际操作GPIOx_IDR寄存…...
做直播网站/聚合搜索引擎入口
1、Annotation 注解版 1.1、应用场景(Student-Teacher):当学生知道有哪些老师教,老师也知道自己教哪些学生时,可用双向关联 1.2、创建Teacher类和Student类 1 package com.shore.model;2 3 import java.util.HashSet…...