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

目录

  • 前言
  • 一、栈的概念及结构
  • 二、栈的实现方式
  • 三、栈的实现
    • 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考研加油!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/953366.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Java+SpringBoot+Vue的前后端分离的体质测试数据分析及可视化设计

基于JavaSpringBootVue的前后端分离的体质测试数据分析及可视化设计 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末附源码…

Github配置ssh key,密钥配对错误怎么解决?

解决密钥配对的方案如下: 方法一、最有效的方案:重新配置,验证 SSH 密钥是否已添加到 GitHub 确保您的 SSH 密钥已经正确添加到了 GitHub 账户中。您可以打开命令行控制台(cmd/powerShell都可以),按照以下…

HarmonyOS鸿蒙开发 弹窗及加载中指示器HUD功能实现

HarmonyOS鸿蒙开发 弹窗及加载中指示器HUD功能实现 最近在学习鸿蒙开发过程中,阅读了官方文档,在之前做flutter时候,经常使用overlay,使用OverlayEntry加入到overlayState来做添加悬浮按钮、提示弹窗、加载中指示器、加载失败的t…

STL之VectorMapList针对erase方法踩坑笔记

前沿 如下总结的三种容器,开头都会涉及当前容器的特点,再者就本次针对erase方法的使用避坑总结。 一.Vector vector关联关联容器,存储内存是连续,且特点支持快速访问,但是插入和删除效率比较地(需要找查找和移动)。另…

【Rust】引用与借用

目录 思维导图 1. 引用与借用的基本概念 1.1. 引用示例 2. 借用的规则 2.1. 可变借用示例 2.2. 借用的限制 3. 引用的生命周期 思维导图 1. 引用与借用的基本概念 引用的定义:引用是一种指向数据的指针,但与裸指针不同,Rust的引用在编…

《自动驾驶与机器人中的SLAM技术》ch8:基于 IESKF 的紧耦合 LIO 系统

紧耦合系统,就是把点云的残差方程直接作为观测方程,写入观测模型中。这种做法相当于在滤波器或者优化算法内置了一个 ICP 或 NDT。因为 ICP 和 NDT 需要迭代来更新它们的最近邻,所以相应的滤波器也应该使用可以迭代的版本,ESKF 对…

Mac 删除ABC 输入法

参考链接:百度安全验证 Mac下删除系统自带输入法ABC,正解!_mac删除abc输入法-CSDN博客 ABC 输入法和搜狗输入法等 英文有冲突~~ 切换后还会在英文状态,可以删除 ;可能会对DNS 输入有影响,但是可以通过复…

1.13 多线程编程

1.思维导图 2.创建两个子进程,父进程负责:向文件中写入数据;两个子进程负责:从文件中读取数据。 要求:一定保证1号子进程先读取,2号子进程后读取,使用文件IO去实现。 1>程序代码 …

Elasticsearch ES|QL 地理空间索引加入纽约犯罪地图

可以根据地理空间数据连接两个索引。在本教程中,我将向你展示如何通过混合邻里多边形和 GPS 犯罪事件坐标来创建纽约市的犯罪地图。 安装 如果你还没有安装好自己的 Elasticsearch 及 Kibana 的话,请参考如下的链接来进行安装。 如何在 Linux&#xff0…

数据分析-使用Excel透视图/表分析禅道数据

背景 禅道,是目前国内用得比较多的研发项目管理系统,我们常常会用它进行需求管理,缺陷跟踪,甚至软件全流程的管理,如果能将平台上的数据结公司的实际情况进行合理的分析利用,相信会给我们的项目复盘总结带来…

【c语言】指针 (完结)

一、sizeof和strlen的对比 1、sizeof 前面我们在学习操作符的时候,我们学习了sizeof,知道其是计算变量所占内存的大小的,单 位是字节,如果操作数是数据类型的话,计算的就是这个类型的变量所占的内存空间的大…

Chromium 132 编译指南 Windows 篇 - 生成构建文件 (六)

1. 引言 在上一篇文章中,我们已经成功获取了 Chromium 的源代码并同步了相关的第三方依赖。本文将继续深入,指导您如何使用 GN 工具生成构建文件,为接下来的编译工作奠定基础。 2. 切换 Chromium 版本至 132 在开始正式构建之前&#xff0…

(12)springMVC文件的上传

SpringMVC文件上传 首先是快速搭建一个springMVC项目 新建项目mvn依赖导入添加webMoudle添加Tomcat运行环境.在配置tomcat时ApplicationContext置为"/"配置Artfact的lib配置WEB-INF配置文件(记得添加乱码过滤)配置springmvc-servlet文件&…

3D目标检测数据集——Waymo数据集

Waymo数据集簡介 发布首页:https://waymo.com/open/ 论文:https://openaccess.thecvf.com/content_CVPR_2020/papers/Sun_Scalability_in_Perception_for_Autonomous_Driving_Waymo_Open_Dataset_CVPR_2020_paper.pdf github:https://github.…

Mysql--运维篇--空间管理(表空间,索引空间,临时表空间,二进制日志,数据归档等)

MySQL的空间管理是指对数据库存储资源的管理和优化。确保数据库能够高效地使用磁盘空间、内存和其他系统资源。良好的空间管理不仅有助于提高数据库的性能,还能减少存储成本并防止因磁盘空间不足导致的服务中断。MySQL的空间管理涉及多个方面,包括表空间…

STM32之LWIP网络通讯设计-下(十五)

STM32F407 系列文章 - ETH-LWIP(十五) 目录 前言 一、软件设计 二、CubeMX实现 1.配置前准备 2.CubeMX配置 1.ETH模块配置 2.时钟模块配置 3.中断模块配置 4.RCC及SYS配置 5.LWIP模块配置 3.生成代码 1.main文件 2.用户层源文件 3.用户层头…

Gateway 网关

1.Spring Cloud Gateway Spring cloud gateway是spring官方基于Spring 5.0、Spring Boot2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供简单、有效和统一的API路由管理方式,Spring Cloud Gateway作为Spring Cloud生态…

数据结构:栈(Stack)和队列(Queue)—面试题(二)

1. 用队列实现栈。 习题链接https://leetcode.cn/problems/implement-stack-using-queues/description/描述: 请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty&a…

在 .NET 9 中使用 Scalar 替代 Swagger

前言 在.NET 9发布以后ASP.NET Core官方团队发布公告已经将Swashbuckle.AspNetCore(一个为ASP.NET Core API提供Swagger工具的项目)从ASP.NET Core Web API模板中移除,这意味着以后我们创建Web API项目的时候不会再自动生成Swagger API文档了…

双模充电桩发展前景:解锁新能源汽车未来的金钥匙,市场潜力无限

随着全球能源转型的浪潮席卷而来,新能源汽车行业正以前所未有的速度蓬勃发展,而作为其坚实后盾的充电基础设施,特别是双模充电桩,正逐渐成为推动这一变革的关键力量。本文将从多维度深入剖析双模充电桩的市场现状、显著优势、驱动…