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

【数据结构】栈的概念、结构和实现详解

本文来介绍一下数据结构中的栈,以及如何用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中自己测试一下,这里就不测试了

到这里这个栈就实现好了,本篇也就结束啦,拜拜~

相关文章:

【数据结构】栈的概念、结构和实现详解

本文来介绍一下数据结构中的栈&#xff0c;以及如何用C语言去实现。 1. 栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;它只允许在固定的一端进行插入和删除元素的操作。 进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。 栈中元素遵循后进先出…...

LeetCode 每日一题 2024/7/29-2024/8/4

记录了初步解题思路 以及本地实现代码&#xff1b;并不一定为最优 也希望大家能一起探讨 一起进步 目录 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中死锁的触发条件&#xff1a; 死锁是当 Goroutine 被阻塞而无法解除阻塞时产生的一种状态。 2.操作系统死锁&#xff1a; 发生死锁时&#xff0c;线…...

c/c++中π怎么定义

c/c中都没有π的专属变量&#xff0c;一般都是自定义。 方法1&#xff1a;#define pi 3.1415926 方法2&#xff1a;使用反三角函数const double pi acos(-1.0);...

基于whisper流式语音识别

为了实现持续监听麦克风并在检测到声音时进行转录&#xff0c;我们可以将流的监听时间设置为无限长。通过使用一个音量门限来检测是否有声音&#xff0c;然后进行转录。 安装依赖 确保安装必要的库&#xff1a; pip install torch torchaudio openai-whisper sounddevice nu…...

Web3 市场暴跌的时候,哪些token跌的少,哪些还涨了? binance 数据爬取及分析

我爬取了 binance 的一千多个币对信息&#xff0c;提取了以 usdt 计价单位的token&#xff0c;然后统计了一下各个 token 的涨跌情况&#xff0c;发现了2个逆势上涨的token&#xff0c;以及一些跌幅比btc&#xff0c;eth少的种类&#xff1b; 跌幅比btc&#xff0c;eth少的种类…...

ffmpeg获得视频的音频文件

要从视频文件中提取音频文件&#xff0c;你可以使用 FFmpeg&#xff0c;这是一个强大的多媒体框架&#xff0c;用于转换、流化以及处理多媒体数据。下面是如何使用 FFmpeg 从视频文件中提取音频的步骤&#xff1a; 1. 确定视频文件的位置&#xff1a; 确保你知道视频文件的完整…...

Robot Operating System——深度解析单线程执行器(SingleThreadedExecutor)执行逻辑

大纲 创建SingleThreadedExecutor新增Nodeadd_nodetrigger_entity_recollectcollect_entities 自旋等待get_next_executablewait_for_workget_next_ready_executableTimerSubscriptionServiceClientWaitableAnyExecutable execute_any_executable 参考资料 在ROS2中&#xff0c…...

【TS】使用npm全局安装typescript

查看npm安装 npm -v 安装typescript npm i -g typescript 查看安装 tsc 这就是标致着安装完成。...

安全用户角色权限

$PATH 搞系统设置设置⾥头path ⽬标包含mysql 可执⾏⽂件&#xff0c;那么就是由使⽤ 在终端使⽤ ./bin/mysql -h192.168.71.164 -P3306 -uroot -proot 1.远程登录前提条件是mysql.user表中的host属性为%&#xff0c;如果是 localhost就不允许远程登录&#xff0c;update…...

代理模式学习

代理模式 代理模式是常用的java设计模式&#xff0c;他的特征是代理类与委托类有同样的接口&#xff0c;代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类&#xff0c;以及事后处理消息等。代理类与委托类之间通常会存在关联关系&#xff0c;一个代理类的对象…...

深入理解Go 语言信号量 Semaphore

1. 什么是信号量 信号量的概念是荷兰计算机科学家 1.1 P/V 操作 Dijkstra 在他的论文中为信号量定义了两个操作 : P 和 V 。 1.2 信号量和互斥锁的区别与联系 信号量有两种类型&#xff1a;二元信号量和计数信号量。 2. 信号量的 channel 实现 程序在运行时&#xff0c;…...

VisualStudio2019下载与安装

1.下载 通过百度网盘分享的文件&#xff1a;VisualStudio2019 链接&#xff1a;https://pan.baidu.com/s/16tqm0ZsOkmXTfGmi4LnGbA 提取码&#xff1a;wx60 --来自百度网盘超级会员V3的分享 2.安装...

李宏毅老师机器学习常见英语词汇

目录 1.Regression &#xff1a;回归2.Classification&#xff1a;分类3.local minima:局部最小值4.saddle point:鞍点5.ground truth:它是机器学习算法的参考标准&#xff0c;用于衡量模型的性的和判断模型的准确性6.optimization:优化 1.Regression &#xff1a;回归 2.Clas…...

人工智能时代,程序员如何保持核心竞争力?

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

WiFi to Ethernet: 树莓派共享无线连接至有线网口,自动通过Captive Poartal网页登录认证

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

【神软大数据治理平台-高级动态SQL(接口开发)】

1、背景 业务部门需大数据平台按照所提需求提供企业数据接口&#xff0c;基于神软大数据治理平台-高级动态SQL功能&#xff0c;满足业务需求&#xff0c;如下&#xff1a; &#xff08;1&#xff09;业务系统需求&#xff1a; 输入&#xff1a; enterpriseName&#xff1a;…...

【Java数据结构】Map和Set超详细两万字讲解(内含搜索树+哈希表)

&#x1f512;文章目录&#xff1a; 1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; 2. Map和Set的基础概念 3.Map的基础使用 4.Set的基础使用 5. TreeMap的本质——红黑树 5.1二叉搜索树的概念 5.2二叉搜索树的模拟实现 二叉搜索树——查找 二…...

中国制造2025,会抛弃精益生产吗?

时至今日&#xff0c;“精益生产”模式依旧大行其道&#xff0c;它始终支持着中国制造业以最低的成本做出优质产品。我们认为&#xff0c;纵然是中国制造2025成为现实&#xff0c;精益生产模式也仍然是整个制造业的精髓之一。 首先&#xff0c;精益生产模式最重要的一根脊梁就是…...

Rust 循环

Rust 循环 在编程语言中&#xff0c;循环是一种重要的控制结构&#xff0c;它允许我们重复执行一段代码直到满足特定的条件。Rust 语言提供了多种循环方式&#xff0c;每种方式都有其特定的用途和语法。本文将详细介绍 Rust 中的循环&#xff0c;包括 loop、while、while let、…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...