【数据结构】栈的概念、结构和实现详解
本文来介绍一下数据结构中的栈,以及如何用C语言去实现。
1. 栈的概念及结构
栈:一种特殊的线性表,它只允许在固定的一端进行插入和删除元素的操作。
进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。
栈中元素遵循后进先出LIFO(Last In First Out)的原则
压栈:栈的插入操作叫做进栈/入栈/压栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈,出数据也在栈顶。
2. 实现栈的底层方法选择
没有规定栈的哪端是栈顶,只说了数据插入和删除的一端是栈顶,所以我们栈的底层实现可以用链表或者数组 。
虽然数组和单链表都可以实现栈,但是单链表能很好入数据不好删除数据,这里单链表要删除数据就是尾删,尾删需要找到前一个结点,不是很方便。
非要用链表的话有两个解决方法,1.可以用双向链表 2.我们把单链表的头节点当作栈顶,也就是把左边当栈顶,右边当栈底,对单链表进行头插和头删的操作。
现在有3种方法实现栈,数组,单链表,双链表,我们应该如何选?
首先排除双链表,用双链表不如用单链表,双链表因为一个节点存两个指针,比单链表的一个节点多了4个字节或者8个字节。数组实现栈和单链表实现栈有什么区别?基本没区别,都可以,非要说选一个,我们还是更倾向于数组,因为数组的唯一缺点就是内存不足时需要扩容,扩容的影响也不是特别大,最重要的是数组的缓存效率更高。所以我们就用数组实现栈。
3. 栈的实现
提前说明,如果本篇看不太懂可以先看看【数据结构】顺序表-CSDN博客,我们栈的实现和顺序表的实现差不多。
还是一样,新建一个头文件和两个源文件
点开Stack.h文件,在这个文件里面我们要定义栈的结构,以及给类型和栈的结构取别名。
typedef int STDateType;
typedef struct Stack
{STDateType* a;//动态申请空间 调大小int top; //用栈顶记录元素个数int capacity; //数组实现要扩容,记录空间大小
}ST;
栈一共要实现下面这7个接口,我们将一个一个来看.
void STInit(ST* pst);//栈初始化
void STDistroy(ST* pst);//栈的销毁
void STPush(ST* pst, STDateType x);//压栈
void STPop(ST* pst);//出栈
STDateType STTopDate(ST* pst);//获取栈顶元素
bool STEmpty(ST* pst);//判断栈是否为空
int STSize(ST* pst);//获取栈元素个数
这里是会用到的头文件,且标注了是什么会用到,被包含的头文件全放在Stack.h中
#include <stdio.h>
#include <stdlib.h> //空间申请
#include <stdbool.h> //布尔类型
#include <assert.h> //断言
在Stack.c中只需要包含Stack.h
#include "Stack.h"
准别工作做好后我们开始实现栈。
3.1 栈的初始化和销毁
在Stack.h中进行函数的声明。这里的参数需要传指针。
void STInit(ST* pst);//栈初始化
void STDistroy(ST* pst);//栈的销毁
在SeqList.c中进行函数的实现
void STInit(ST* pst)//栈初始化
{assert(pst); //判断pst是否为空pst->capacity = 0;pst->top = 0;pst->a = NULL;
}
void STDistroy(ST* pst)//栈的销毁
{assert(pst);free(pst->a);pst->a = NULL;pst->capacity = pst->top = 0;
}
这里和顺序表差不多,很简单就不多说了。
3.2 压栈和出栈
在Stack.h中进行函数的声明。
void STPush(ST* pst, STDateType x);//压栈
void STPop(ST* pst);//出栈
这里的参数需要传指针,压栈的函数参数还有要插入的数据。因为栈插入数据就是从栈顶插入,这里就没有什么头插尾插的概念,直接就是Push,删除数据也是,直接栈顶删除Pop。
在SeqList.c中进行函数的实现
先说压栈
先分析空间足够的情况,初始化我们把top置为0,放进一个元素,top就是1,但是这个元素在数组中的下标为0,所以栈顶元素数组下标是top-1,而top指向的是栈顶元素的下一个位置,而不是栈顶元素。
所以我们放数据就是直接放下标为top的位置。
void STPush(ST* pst, STDateType x)//压栈
{assert(pst);pst->a[pst->top] = x; //先放数据pst->top++; //然后top++}
然后考虑扩容。capacity是数组大小,分析一下数组满了的情况
应该是top和capacity相等,此时就要扩容。
void STPush(ST* pst, STDateType x)//压栈
{assert(pst);if (pst->top == pst->capacity){int newcapacity = pst->capacity * 2;//原来空间2倍的扩容//扩容STDateType* tmp = (STDateType*)realloc(pst->a, newcapacity * sizeof(STDateType));if (tmp == NULL) //扩容失败{perror("realloc fail");return;}//扩容成功pst->a = tmp;pst->capacity = newcapacity;}pst->a[pst->top] = x;pst->top++;
}
但是我们要注意这句代码 int newcapacity = pst->capacity * 2; 我们一开始capacity初始化为0,0乘任何数都是0,所以这句换我们要改一下,用一个三目操作符就能解决。
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
再说出栈
出栈和顺序表是一样的,直接top--就行了,不理解的可以看看顺序表中数据的尾删
void STPop(ST* pst)//出栈
{assert(pst);assert(pst->top > 0);pst->top--;
}
在test.c中测试一下栈的初始化,压栈,出栈,栈的销毁。
#include "Stack.h"
int main()
{ST s;STInit(&s); //初始化STPush(&s, 1);//压栈STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);for (int i = 0; i < s.top; i++){printf("%d ", s.a[i]);}printf("\n");STPop(&s);//出栈for (int i = 0; i < s.top; i++){printf("%d ", s.a[i] );}printf("\n");STPop(&s);//出栈for (int i = 0; i < s.top; i++){printf("%d ", s.a[i]);}STDistroy(&s);//销毁return 0;
}
遵循后进先出
3.3 获取栈顶元素
在Stack.h中进行函数的声明。
STDateType STTopDate(ST* pst);//获取栈顶元素
在SeqList.c中进行函数的实现
STDateType STTopDate(ST* pst)//获取栈顶元素
{assert(pst);assert(pst->top > 0);return pst->a[pst->top - 1];
}
在test.c中测试一下
#include "Stack.h"
int main()
{ST s;STInit(&s); //初始化STPush(&s, 1);//压栈STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);for (int i = 0; i < s.top; i++){printf("%d ", s.a[i]);}printf("\n");printf("栈顶元素:%d\n", STTopDate(&s));STPop(&s);//出栈STPop(&s);for (int i = 0; i < s.top; i++){printf("%d ", s.a[i] );}printf("\n");printf("栈顶元素:%d\n", STTopDate(&s));STDistroy(&s);//销毁return 0;
}
3.4 判断栈是否为空
在Stack.h中进行函数的声明。
bool STEmpty(ST* pst);//判断栈是否为空
这里用了bool类型,需要包含头文件stdbool.h
在SeqList.c中进行函数的实现
bool STEmpty(ST* pst)//判断栈是否为空
{assert(pst);if (pst->top == 0) //为空,返回真return true;else //不为空,返回假return false;
}
还有一个更简单的写法,如下
bool STEmpty(ST* pst)//判断栈是否为空
{assert(pst);return pst->top == 0;
}
为空,返回真,不为空返回假。
在test.c中测试一下,用栈的后进先出的特点访问
#include "Stack.h"
int main()
{ST s;STInit(&s); //初始化STPush(&s, 1);//压栈STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);//栈标准的后进先出访问方式while (!STEmpty(&s)){printf("%d ", STTopDate(&s));//先访问栈顶元素STPop(&s); //然后把栈顶元素删除}STDistroy(&s);//销毁return 0;
}
3.5 获取栈元素个数
在Stack.h中进行函数的声明。
int STSize(ST* pst);//获取栈元素个数
在SeqList.c中进行函数的实现
因为我们前面的top初始化为0,所以top就是栈的元素个数,直接返回top就行了。
int STSize(ST* pst)//获取栈元素个数
{assert(pst);return pst->top;
}
在test.c中自己测试一下,这里就不测试了
到这里这个栈就实现好了,本篇也就结束啦,拜拜~
相关文章:

【数据结构】栈的概念、结构和实现详解
本文来介绍一下数据结构中的栈,以及如何用C语言去实现。 1. 栈的概念及结构 栈:一种特殊的线性表,它只允许在固定的一端进行插入和删除元素的操作。 进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。 栈中元素遵循后进先出…...
LeetCode 每日一题 2024/7/29-2024/8/4
记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步 目录 7/29 682. 棒球比赛7/30 2961. 双模幂运算7/31 3111. 覆盖所有点的最少矩形数目8/1 LCP 40. 心算挑战8/2 3128. 直角三角形8/3 3143. 正方形中的最多点数8/4 572. 另一棵树…...

Golang死锁vs操作系统死锁
目录 一、死锁 二、Golang死锁场景 2.1 重复上锁 2.2 不会减少的 WaitGroup 2.3 空select 2.4 channel 一、死锁 1.golang中死锁的触发条件: 死锁是当 Goroutine 被阻塞而无法解除阻塞时产生的一种状态。 2.操作系统死锁: 发生死锁时,线…...
c/c++中π怎么定义
c/c中都没有π的专属变量,一般都是自定义。 方法1:#define pi 3.1415926 方法2:使用反三角函数const double pi acos(-1.0);...
基于whisper流式语音识别
为了实现持续监听麦克风并在检测到声音时进行转录,我们可以将流的监听时间设置为无限长。通过使用一个音量门限来检测是否有声音,然后进行转录。 安装依赖 确保安装必要的库: pip install torch torchaudio openai-whisper sounddevice nu…...

Web3 市场暴跌的时候,哪些token跌的少,哪些还涨了? binance 数据爬取及分析
我爬取了 binance 的一千多个币对信息,提取了以 usdt 计价单位的token,然后统计了一下各个 token 的涨跌情况,发现了2个逆势上涨的token,以及一些跌幅比btc,eth少的种类; 跌幅比btc,eth少的种类…...
ffmpeg获得视频的音频文件
要从视频文件中提取音频文件,你可以使用 FFmpeg,这是一个强大的多媒体框架,用于转换、流化以及处理多媒体数据。下面是如何使用 FFmpeg 从视频文件中提取音频的步骤: 1. 确定视频文件的位置: 确保你知道视频文件的完整…...

Robot Operating System——深度解析单线程执行器(SingleThreadedExecutor)执行逻辑
大纲 创建SingleThreadedExecutor新增Nodeadd_nodetrigger_entity_recollectcollect_entities 自旋等待get_next_executablewait_for_workget_next_ready_executableTimerSubscriptionServiceClientWaitableAnyExecutable execute_any_executable 参考资料 在ROS2中,…...

【TS】使用npm全局安装typescript
查看npm安装 npm -v 安装typescript npm i -g typescript 查看安装 tsc 这就是标致着安装完成。...
安全用户角色权限
$PATH 搞系统设置设置⾥头path ⽬标包含mysql 可执⾏⽂件,那么就是由使⽤ 在终端使⽤ ./bin/mysql -h192.168.71.164 -P3306 -uroot -proot 1.远程登录前提条件是mysql.user表中的host属性为%,如果是 localhost就不允许远程登录,update…...
代理模式学习
代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象…...
深入理解Go 语言信号量 Semaphore
1. 什么是信号量 信号量的概念是荷兰计算机科学家 1.1 P/V 操作 Dijkstra 在他的论文中为信号量定义了两个操作 : P 和 V 。 1.2 信号量和互斥锁的区别与联系 信号量有两种类型:二元信号量和计数信号量。 2. 信号量的 channel 实现 程序在运行时,…...
VisualStudio2019下载与安装
1.下载 通过百度网盘分享的文件:VisualStudio2019 链接:https://pan.baidu.com/s/16tqm0ZsOkmXTfGmi4LnGbA 提取码:wx60 --来自百度网盘超级会员V3的分享 2.安装...
李宏毅老师机器学习常见英语词汇
目录 1.Regression :回归2.Classification:分类3.local minima:局部最小值4.saddle point:鞍点5.ground truth:它是机器学习算法的参考标准,用于衡量模型的性的和判断模型的准确性6.optimization:优化 1.Regression :回归 2.Clas…...

人工智能时代,程序员如何保持核心竞争力?
人工智能时代,程序员如何保持核心竞争力? 随着AIGC(如chatgpt、midjourney、claude等)大语言模型接二连三的涌现,AI辅助编程工具日益普及,程序员的工作方式正在发生深刻变革。有人担心AI可能取代部分编程工…...

WiFi to Ethernet: 树莓派共享无线连接至有线网口,自动通过Captive Poartal网页登录认证
物联网开发系列:物联网开发之旅① WiFi to Ethernet: 树莓派共享无线连接至有线网口,自动通过Captive Poartal验证物联网开发番外篇之 Captive Portal验证原理 文章目录 背景实现工具实现细节一、将无线连接共享到以太网1. 配置静态IP地址2. 启用IP转发3…...

【神软大数据治理平台-高级动态SQL(接口开发)】
1、背景 业务部门需大数据平台按照所提需求提供企业数据接口,基于神软大数据治理平台-高级动态SQL功能,满足业务需求,如下: (1)业务系统需求: 输入: enterpriseName:…...

【Java数据结构】Map和Set超详细两万字讲解(内含搜索树+哈希表)
🔒文章目录: 1.❤️❤️前言~🥳🎉🎉🎉 2. Map和Set的基础概念 3.Map的基础使用 4.Set的基础使用 5. TreeMap的本质——红黑树 5.1二叉搜索树的概念 5.2二叉搜索树的模拟实现 二叉搜索树——查找 二…...

中国制造2025,会抛弃精益生产吗?
时至今日,“精益生产”模式依旧大行其道,它始终支持着中国制造业以最低的成本做出优质产品。我们认为,纵然是中国制造2025成为现实,精益生产模式也仍然是整个制造业的精髓之一。 首先,精益生产模式最重要的一根脊梁就是…...
Rust 循环
Rust 循环 在编程语言中,循环是一种重要的控制结构,它允许我们重复执行一段代码直到满足特定的条件。Rust 语言提供了多种循环方式,每种方式都有其特定的用途和语法。本文将详细介绍 Rust 中的循环,包括 loop、while、while let、…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...

基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...