【数据结构】栈篇

news2024/9/27 17:30:45

文章目录

  • 1. 栈
  • 2. 栈的实现
    • 2.1 准备工作
    • 2.2 栈的初始化
    • 2.3 入栈
    • 2.4 出栈
    • 2.5 判断栈是否为空
    • 2.6 取出栈顶元素
    • 2.7 获取栈中有效元素个数
    • 2.8 销毁栈
    • 效果图
  • 3.代码整合

1. 栈

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

后进先出(Last In First Out)
后进先出

上图展示了栈的入栈与出栈,只有一端开口是栈的特点。

2. 栈的实现

要实现栈其实很简单,可以说栈就是功能缺失的顺序表。另外栈也分为数组栈和链栈。
栈的实现

链栈和数组栈也没什么特别的优劣之分,不过如果你要实现单链表的链栈,就要让链表的头为栈顶,这样插入数据的时候就不用找尾了。

栈的实现

相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。下面我们来实现数组栈。

2.1 准备工作

为了以后用这个栈来存储各种类型的数据,所以我们定义一个标识符Datatype。
为了定位栈顶还需要一个top变量来指向栈的顶部。以及记录当前开了多少空间的capacity。

#define Datatype int //增加代码的拓展性
typedef struct stack
{
	Datatype* a;
	int top;
	int capacity;
}stack;

2.2 栈的初始化

在初始化中可以先malloc一些空间,不过这里我就直接初始化为NULL了,后面插入时在扩容。
关于top
如果你初始化为-1代表的意思就是栈顶元素,如果你初始化为0代表的意思就是栈顶元素的下一个元素。
无论你初始化哪个都是可以的,只是后续的实现代码有些许不同。这里我选的就是初始化为0

void InitStack(stack* ps)
{
	ps->a = NULL;//可以初始时就开辟空间,不过我选择了初始不开空间的写法
	ps->top = 0;//初始为-1或者0都可以,后面的操作根据top的初始值来操作。
	ps->capacity = 0;
}

2.3 入栈

每次入栈都要判断当前的空间是否足够,你也可以像写顺序表时那样把,判断写成一个函数,不过因为栈就只有一个插入函数,就显的没什么必要了。
扩容时要注意,因为初始化时的capacity为0不能直接X2扩容,要判断一下。
另外可能会有人有疑问,realloc不是用来扩容的吗,还可以当malloc用吗?
可以的,当没有初始空间时,realloc就可以当malloc使用。

//栈的插入/入栈
void PushStack(stack* ps,Datatype x)
{
	assert(ps);
	//判断空间是否充足
	if (ps->top == ps->capacity)
	{
		//空间不够,扩容
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//防止0乘任何数都为0的情况
		Datatype* tmp = (stack*)realloc(ps->a,sizeof(stack) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top += 1;
}

2.4 出栈

删除是唯一要注意的就是,如果栈为空可不能再删除了。

//栈的删除/出栈
void PopStack(stack* ps)
{
	assert(ps);
	assert(ps->top > 0);//栈为空时不能删除
	ps->top -= 1;
}

2.5 判断栈是否为空

注意为空返回真,不为空返回假。
这里只要判断ps->top是否为0就可以了。

bool EmptyStack(stack* ps)
{
	assert(ps);
	return ps->top == 0;
}

2.6 取出栈顶元素

因为ps->top表示栈顶的下一个元素,所以返回ps->a[ps->top - 1]就可以了

//取出栈顶元素
Datatype TopStack(stack* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

2.7 获取栈中有效元素个数

不知道大家有没有认为,ps->top很像顺序表的size。这里我们直接返回ps->top就可以代表有效个数了。

//获取栈中有效元素个数
int SizeStack(stack* ps)
{
	assert(ps);
	return ps->top;
}

2.8 销毁栈

为了防止内存泄漏,一定要记得释放空间哦~

//销毁栈
void DestoryStack(stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

效果图

效果图

3.代码整合

//stack.h
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>

#define Datatype int //增加代码的拓展性
typedef struct stack
{
	Datatype* a;
	int top;
	int capacity;
}stack;

//栈的初始化
void InitStack(stack* ps);

//栈的插入/入栈
void PushStack(stack* ps, Datatype x);

//栈的删除/出栈
void PopStack(stack* ps);

//判断栈是否为空
bool EmptyStack(stack* ps);

//取出栈顶元素
Datatype TopStack(stack* ps);

//获取栈中有效元素个数
int SizeStack(stack* ps);

//销毁栈
void DestoryStack(stack* ps);

//stack.c
#include "stack.h"

//栈的初始化
void InitStack(stack* ps)
{
	ps->a = NULL;//可以初始时就开辟空间,不过我选择了初始不开空间的写法
	ps->top = 0;//初始为-1或者0都可以,后面的操作根据top的初始值来操作。
	ps->capacity = 0;
}

//栈的插入/入栈
void PushStack(stack* ps,Datatype x)
{
	assert(ps);
	//判断空间是否充足
	if (ps->top == ps->capacity)
	{
		//空间不够,扩容
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//防止0乘任何数都为0的情况
		Datatype* tmp = (stack*)realloc(ps->a,sizeof(stack) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top += 1;
}


//栈的删除/出栈
void PopStack(stack* ps)
{
	assert(ps);
	assert(ps->top > 0);//栈为空时不能删除
	ps->top -= 1;
}

//判断栈是否为空
bool EmptyStack(stack* ps)
{
	assert(ps);
	return ps->top == 0;
}

//取出栈顶元素
Datatype TopStack(stack* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

//获取栈中有效元素个数
int SizeStack(stack* ps)
{
	assert(ps);
	return ps->top;
}

//销毁栈
void DestoryStack(stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

//test.c
#include "stack.h"

void test1()
{
	/*stack s;
	InitStack(&s);
	PushStack(&s, 1);
	PushStack(&s, 1);
	PushStack(&s, 2);
	PushStack(&s, 2);
	PushStack(&s, 3);
	PushStack(&s, 3);
	PushStack(&s, 4);
	PushStack(&s, 4);

	while (!EmptyStack(&s))
	{
		printf("%d ", TopStack(&s));
		PopStack(&s);
	}
	DestoryStack(&s);*/
}

int main()
{
	test1();
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

qt项目之在线考试系统----------MVC使用模型-视图-控制器

1、什么是MVC的设计模式 在Qt中,MVC是一种设计模式,全称为Model-View-Controller(模型-视图-控制器)。这是一个经典的设计模式,用于将数据表示(Model)、用户界面(View)和业务逻辑(Controller)分离。具体来说,MVC设计模式在Qt中的应用如下: Model(模型):表示应用…

C++之从C过渡(下)

C之从C过渡&#xff08;下&#xff09; 接着上一篇&#xff0c;从引用开始往下讲解。 引用的特性 引⽤在定义时必须初始化⼀个变量可以有多个引⽤引⽤⼀旦引⽤⼀个实体&#xff0c;再不能引⽤其他实体 C的引用不能完全替代指针。比如&#xff0c;在链表结点中我们会存储指向下…

2024下半年EI收录的老牌会议,检索超快!

在科研领域&#xff0c;EI作为全球公认的工程技术领域重要检索工具&#xff0c;其收录的会议论文往往代表着某一领域内的最新研究成果与前沿技术。对于广大科研工作者而言&#xff0c;能够在EI收录的老牌会议上发表论文&#xff0c;不仅是对自身研究能力的一种肯定&#xff0c;…

pinctrl子系统做功能的切换.

SD卡和debug口中sdmmc和uart共用同一组pin脚,需实现在sd使用的时候切换到sdmmc不插入sd卡的时候使用debug口功能。 sd卡有检测脚可以作为切换的标志所以我们的切换要在sd卡的驱动中去做。 第一步&#xff1a; 使能俩个功能的dts并去除不能切换的pinctrl&#xff0c;只有一个节点…

自动回复的AI小助手,人工智能还是人工智障

最近在运营公司的百家号账号。因为老杨和同事们在一些大会上有干货满满的演讲&#xff0c;我们将它剪辑成比较短的视频&#xff0c;放在一些平台上供大家观看。百家号因百度的关系&#xff0c;搜索的引流会好一些。 一开始每次发好视频&#xff0c;就会有播放量。几次之后&…

Java每日一题———删除有序数组中的重复项

这个问题可以通过使用双指针技术来解决。我们可以使用两个指针&#xff0c;一个慢指针 slowRunner 用于跟踪新数组的末尾&#xff0c;另一个快指针 fastRunner 用于遍历数组。每当 fastRunner 遇到一个新的唯一元素时&#xff0c;就将其复制到 slowRunner 指向的位置&#xff0…

创建谷歌外链的常见错误及避免方法!

创建谷歌外链是个技术活&#xff0c;很多人在这个过程中容易犯错。了解这些常见错误和如何避免它们可以帮助你更有效地提升你的SEO表现。 其一&#xff0c;忽视锚文本多样性。有些人在建立外链时&#xff0c;总是使用相同的锚文本&#xff0c;这看起来很不自然&#xff0c;可能…

基于python爬虫技术的bilibili网用户数据采集系统的设计与实现-计算机毕业设计源码55962

摘要 在当今信息爆炸的时代&#xff0c;互联网已经成为人们获取信息、交流思想的重要平台。作为国内领先的弹幕视频网站&#xff0c;Bilibili凭借其独特的弹幕文化和丰富的内容生态&#xff0c;吸引了亿万用户的关注。这些用户生成的海量数据蕴含着丰富的信息&#xff0c;对于理…

异常(Java)

目录 1. 异常的概念 2. 异常的分类 3. 异常的处理 4. 异常的抛出 5. 异常的捕获 5.1 异常声明throws 5.2 try-catch捕获并处理 5.3 finally 6. 异常的处理流程 7. 自定义异常类 1. 异常的概念 异常就是在程序执行过程中发生的不正常的行为.异常中断了正在执行程序的…

Cross-Modality Person Re-identification with Memory-Based Contrastive Embedding

文章目录 题目&#xff1a;Cross-Modality Person Re-identification with Memory-Based Contrastive Embedding&#xff08;基于记忆对比嵌入的跨模态人物再识别&#xff09;摘要论文分析网络框架1、Problem Definition&#xff08;模态预处理&#xff09;2、Learning Modalit…

RUM技术探索:前端监控数据采集与实践

​​随着互联网技术的不断演进&#xff0c;Web应用程序正日益呈现出复杂多变与高度动态性的特征。用户渴望获得快速的页面加载、流畅的交互体验以及高度的可靠性。为了满足这些&#xff0c;实时监控 Web 应用的性能和行为变得至关重要。前端监控让开发者能够深入了解应用的表现…

Hack The Box-Resource

总体思路 phar反序列化->SSH CA私钥泄露->SSH CA私钥滥用->SSH脚本滥用 信息收集&端口利用 nmap -sSVC itrc.ssg.htb目标开放了两个ssh端口和一个80端口&#xff0c;先查看80端口 网站是一个SSG IT资源中心&#xff0c;主要用于解决网站问题、管理 SSH 访问、清…

免费【2024】springboot 付费自习室管理系统的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

正点原子imx6ull-mini-Linux驱动之Linux 音频驱动实验

虽然mini板子没有这个资源&#xff0c;但是可以学学移植这个软件。 音频是我们最常用到的功能&#xff0c;音频也是 linux 和安卓的重点应用场合。I.MX6ULL 带有 SAI 接口&#xff0c;正点原子的 I.MX6ULL ALPHA 开发板通过此接口外接了一个 WM8960 音频 DAC 芯片&#xff0c;…

《程序猿入职必会(10) · SpringBoot3 整合 MyBatis-Plus》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

使用samba在ubuntu和windows之间共享文件

1、在ubuntu上安装samba 在终端输入命令 sudo apt update sudo apt install samba 2、配置samba 打开samba 的配置文件 sudo nano /etc/samba/smb.conf 在文件末尾添加以下内容 [shared] path /home/lzx available yes valid users lzx read only no browsable yes…

【Redis进阶】Redis的持久化RDB和AOF

目录 持久化 RDB持久化 概念 原理 RDB 持久化的详细工作流程 1触发持久化&#xff1a; 2创建子进程&#xff1a; 3数据写入 RDB 文件&#xff1a; 4替换旧文件&#xff1a; 5回收子进程&#xff1a; RDB持久化的触发方式 1.手动触发&#xff1a; 2.自动触发&#…

鸿蒙应用服务开发【获取天气数据】

获取天气数据 介绍 Weather Service Kit&#xff08;天气服务&#xff09;是鸿蒙生态下的一个数据提供服务&#xff0c; Weather Service Kit融合了多家气象行业TOPs供应商&#xff0c;提供专业、精准、稳定的超本地化天气数据服务&#xff0c; 开发者可以通过Weather Servic…

Tomcat 漏洞

1.CVE-2017-12615 抓包&#xff0c;将get改为put jsp文件后加/ 访问木马使用蚁剑连接 2.弱口令 点击后输入默认用户名、密码&#xff1a;tomcat/tomcat 登录成功&#xff0c;在文件上传位置上传war包 使用哥斯拉生成一个jsp木马&#xff0c;打包&#xff0c;改后缀为war,上传…