初阶数据结构【栈及其接口的实现】

server/2025/1/14 9:42:15/

目录

  • 前言
  • 一、栈的概念及结构
  • 二、栈的实现方式
  • 三、栈的实现
    • 3.1 基本结构
    • 3.2 栈的基本功能接口
      • 栈的初始化
      • 栈的销毁
    • 3.3 入栈接口
    • 3.4 出栈接口
    • 3.5 栈的其它接口
      • 获取数据的个数接口
      • 栈判断是否为空接口
      • 获取栈顶数据接口
    • 注:为什么要实现这些简单的接口,直接调用结构体不好吗?
    • 3.6 测试
  • 总结


前言

前面我们学习并实现了顺序表与链表的接口,顺序表与链表都是线性表的一种,即在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的;这期我们继续来介绍一种特殊的线性表——


一、栈的概念及结构

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

  • 压栈:栈的插入操作叫做进栈/压栈/入栈入数据在栈顶
  • 出栈:栈的删除操作叫做出栈出数据也在栈顶

在这里插入图片描述
在这里插入图片描述

二、栈的实现方式

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小;所以这里我们用数组的方式来实现;

  • 数组的方式实现:
    • 数组的起始作为栈底,栈底在数组的右边;
      在这里插入图片描述
  • 链表的方式来实现
    • 如果是双向链表:链表的头和尾都可以作为栈顶:

在这里插入图片描述

  • 如果用单链表:由于在出栈的时候我们要寻找下一个栈顶比较麻烦,所以我们只能将栈顶设置在头结点:

在这里插入图片描述
这里我们用数组的方式实现:

三、栈的实现

3.1 基本结构

我们用数组的方式实现栈还分为以下两种:

  • 定长的静态栈
  • 可以动态增长的动态栈

在实际项目应用中,当我们知道这个项目一定不会超过一个固定的内存大小(如1000个整形空间),我们就可以使用这种结构:

#define N 1000//定长的静态栈
typedef struct Stack
{STDataType a[N];int _top; //栈顶
}Stack;

反之,我们用动态增长的栈:

// 支持动态增长的栈
typedef struct Stack
{STDataType* _a;int _top;  //栈顶指针int _capacity; //动态容量
}Stack;

这里我们实现用动态增长的栈

3.2 栈的基本功能接口

栈的初始化

这个接口没什么好说的,不过我们要注意一下capacity的初始值,以便后面入栈的时候方便扩容:

//栈的初始化
void StackInit(Stack* pst)
{assert(pst);//这样会给后面扩容造成麻烦/*pst->_a = NULL;pst->_top = 0;pst->_capacity = 0;*/pst->_a = (STDataType*)malloc(sizeof(STDataType) * 4);pst->_top = 0;pst->_capacity = 4;
}

栈的销毁

//栈的销毁
void StackDestory(Stack* pst)
{assert(pst);free(pst->_a); //释放开辟的空间pst->_a = NULL;pst->_top = pst->_capacity = 0;
}

3.3 入栈接口

根据前面顺序表的插入操作我们也可以得知,在入栈之前我们需要进行扩容判断,再进行入栈操作:
在这里插入图片描述

void StackPush(Stack* pst, STDataType x)
{//检查是否需要扩容if (pst->_top == pst->_capacity){pst->_capacity *= 2;STDataType* tmp = (STDataType*)realloc(pst->_a, sizeof(STDataType) * pst->_capacity);if (tmp == NULL){perror("relloc");exit(-1);}else{pst->_a = tmp;}}pst->_a[pst->_top] = x;pst->_top++;
}

这时候有聪明的同学有个问题了:为什么不将这个扩容代码写出一个函数接口呢?其实你实现完整个栈就会发现,只有这会用到空间的扩容的;所以我们不再将其单独开辟一个接口;

3.4 出栈接口

void StackPop(Stack* pst)
{assert(pst);assert(pst->_top > 0); //判断一下是否为空栈//只需要将top一走,可以不用初始化出栈的值//pst->_a[pst->_top] = 0;pst->_top--;
}

3.5 栈的其它接口

获取数据的个数接口

我们要清楚top的值就是数据的个数

//获取数据的个数
int StackSize(Stack* pst)   //考虑到内存和统一性问题我们这里也传一级指针
{assert(pst);return pst->_top;
}

栈判断是否为空接口

//判空,1是空,0是非空
int StackEmpty(Stack* pst)
{assert(pst);//return pst->_top == 0 ? 1 : 0;return !pst->_top;     //巧写,更加简便
}

获取栈顶数据接口

//获取栈顶数据
STDataType StackTop(Stack* pst)
{assert(pst);assert(pst->_top);return pst->_a[pst->_top-1];
}

注:为什么要实现这些简单的接口,直接调用结构体不好吗?

我们要知道每个人实现相同的结构并不是完全相同的,就像这个栈的实现:

我们在初始化的时候默认pst->_top=0,这就导致我们访问栈顶数据是pst->_a[pst->_top-1];
如果有人在初始化的时候默认pst->_top=-1(也是允许的),那我们访问栈顶数据是pst->_a[pst->_top];

在实际开发中就找成许多麻烦了;

3.6 测试

注:细心的同学会发现我们并没有在前面写栈的打印接口,这是因为栈必须满足先进后出的规律,我们在读取栈顶元素候必须要经过出栈才能继续向下读取;

在这里插入图片描述

所以我们一般这样打印出栈里的元素:

while (!StackEmpty(st))
{printf("%d ", StackTop(st));StackPop(st);   //出栈
}
printf("\n");

我们写出测试函数:

void Test1() {Stack st;StackInit(&st); //初始化StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);//遍历while (!StackEmpty(&st)){printf("%d ", StackTop(&st));StackPop(&st);   //出栈}printf("\n");printf("%d ", StackSize(&st)); //打印大小,已经全部出栈,size为0;StackDestory(&st);  //销毁
}

运行Test1函数结果:
在这里插入图片描述
符合预想结果,测试通过。


总结

这期我们用C语言实现了栈的几个基本接口,我们有了前面的基础,我们发现这并不是很难;下期见!
26考研加油!



http://www.ppmy.cn/server/158249.html

相关文章

springboot国际化

使用springboot开发程序时,如果有国际市场的需求,一般要考虑国际化,在spring中本身对国际化就有很好的支持,下面介绍如何使用springboot开发国际化服务。 正常来说,引入 spring-boot-starter-web 模块后自动就会包括了…

Springboot内置Apache Tomcat 安全漏洞(CVE-2024-50379)

背景 大家都知道我们使用Springboot开发后,无需再额外配置tomcat,因为Springboot已经帮我们内置好了tomcat。 这次在线上安全团队就扫出来了我们Springboot服务的tomcat漏洞: 可以看到这是2023年的洞,Apache Tomcat 安全漏洞(…

STM32标准库学习笔记(十)SPI

前言 学习永无止境!本篇是嵌入式开发之片上外设SPI,了解基本硬件原理以及通信协议。 注:本文章为学习笔记,部分图片与文字来源于网络/江协科技课程/手册,如侵权请联系!谢谢! 一、SPI通信概述 1.…

Node.js——path(路径操作)模块

个人简介 👀个人主页: 前端杂货铺 🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展 📃个人状态: 研发工程师,现效力于中国工业软件事业 🚀人生格言: 积跬步…

基于Android的校园自助打印系统的设计与实现

博主介绍:java高级开发,从事互联网行业多年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了多年的设计程序开发,开发过上千套设计程序,没有什么华丽的语言,只有实…

使用 Python 实现自动化办公(邮件、Excel)

目录 一、Python 自动化办公的准备工作 1.1 安装必要的库 1.2 设置邮件服务 二、邮件自动化处理 2.1 发送邮件 示例代码 注意事项 2.2 接收和读取邮件 示例代码 三、Excel 自动化处理 3.1 读取和写入 Excel 文件 示例代码 3.2 数据处理和分析 示例代码 四、综合…

docker的学习

理解 我对docker的理解:docker其实就是一个服务,需要进行启动还有关闭。 对镜像的理解:镜像相当于一个安装包(可以理解为压缩文件,所以需要从网络上进行下载),镜像下载完之后就要对其运行。运…

【机器学习:十一、神经网络的数学表达式】

神经网络的数学表达式是机器学习的重要理论基础,通过数学语言描述神经网络的结构和工作原理,有助于理解其运算过程、优化方法和性能改进。以下从背景意义、数学表达的重要性、隐藏层与输出层的数学表达,再到二层神经网络的数学表达&#xff0…