数据结构—栈和队列

news2024/9/24 11:28:01

一、栈的概念及结构

栈(Stack)是一种特殊的线性表,其只允许在表的固定的一端进行插入和删除操作。

栈顶:进行插入数据和删除数据的一端。

栈底:相对于栈顶的另一端。

原则:栈的数据元素遵循后进先出LIFO(  Last In First Out )的原则 

通过图也能明显看出栈的特点只有当栈顶元素出栈后,才能出后面的元素,有点类似于手枪的弹夹,用的时候先一个一个把子弹压进去,最后压进去的子弹最先打出,所以有的进栈也称为压栈。

二、栈的实现

栈通常使用顺序存储结构来实现,因为像数组结构在末尾增加或者删除数据就比较方便。

需要实现的接口:

在使用栈时为了有足够的空间我们采用的动态开辟数组的方式来进行数据存储。

1.  栈的初始化与销毁

//初始化栈
void STInit(ST* ps)
{
	assert(ps);
	ps->a = (STDataType*)malloc(sizeof(STDataType) * DEFAULT_SIZE);
	if (NULL == ps->a)
	{
		perror("STInit fail");
		exit(1);
	}
	ps->capcity = DEFAULT_SIZE;
	ps->top = 0;
}
//销毁栈
void STDestroy(ST* ps)
{
	assert(ps);
	free(ps->a);
}

初始化时我们会对栈分配初始化的空间,初始化大小可以根据需要自行更改。

注意:在初始化栈中特别要注意top指向的位置,例如我这里栈顶top赋值的是0,就默认top指向的是0表示栈顶元素的下一个位置。有的地方初始化的是-1,那就表示top指向的是栈顶元素的位置。

2.  入栈和出栈

//入栈
void STPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capcity) //检查是否已满,已满则扩容
	{
		STDataType* empty = (STDataType*)realloc(ps->a, sizeof(STDataType) * (2 * ps->capcity));
		if (empty == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->a = empty;
		ps->capcity = 2 * ps->capcity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
//出栈
void STPop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));
	ps->top--;
}

注意

入栈的时候主要注意的就是判断空间是否存满,判断依据和top有关,如果初始化top=-1则这里的判断条件应该是ps->top+1 == ps->capacity

出栈时需要判断是否为空,栈内元素若已为空还继续出栈则会出栈失败。并且在实现出栈的方法里,并不需要销毁栈顶的元素,只用改变栈顶指针指向的位置,使得无法访问到栈顶元素,也就实现了“销毁”的效果。

3.  其余操作

//返回栈顶元素
STDataType STTop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));
	return ps->a[ps->top - 1];
}
//返回元素个数
int STSize(ST* ps)
{
	assert(ps);
	return ps->top;
}
//判断是否已为空
bool STEmpty(ST* ps)
{
	assert(ps);

	return ps->top == 0;
}

4.  性能分析

顺序栈的入栈和出栈都比较简单,时间复杂度为均为O(1),但在空间上顺序存储的栈需要一大片连续的空间,对于内存浪费比较大,如果当栈满时还会进行扩容也会影响时间效率。如果换做链式栈则会有所改善,空间利用率会更高。总之不同的实现方式有他各自的优势。

队列

一、队列的概念及结构

队列(Queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

队头:进行数据删除操作的一端。

队尾:进行数据插入操作的一端。

原则:队列具有先进先出FIFO( First In First Out )的原则。

队列在我们生活中也非常常见,像我们在各种场所需要进行排队,一般情况下都是先来先进入队列依次进行一系列活动。

二、队列的实现

队列的实现我们也可以采用顺序结构或者链式结构,但因为需要在队头进行操作,所以链式结构是比较好的,下面我将用单链表的形式实现队列。

需要实现的接口:

1.  队列的初始化和销毁

//初始化
void QueueInit(Queue* pq)
{
	assert(pq);

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

//销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

因为我们在实现队列时定义了两个结构体,所以在销毁时需要先把队列中每个结点依次释放掉,再将头和尾指针置为空。

2. 入队列及出队列

入队列代码:

//入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* node = (QNode*)malloc(sizeof(QNode));//为新入队的元素申请一个结点空间
	if (node == NULL)
	{
		perror("malloc fail");
		return;
	}
	node->next = NULL;
	node->data = x;

	if (pq->head == NULL)//如果第一次入队则需将头尾指针指向该结点
	{
		assert(pq->tail == NULL);
		pq->head = pq->tail = node;
	}
	else
	{
		pq->tail->next = node;
		pq->tail = node;
	}
	pq->size++;
}

出队列代码;

//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head && pq->tail);//判断有没有元素
	if (pq->head->next == NULL)//判断是否为仅有一个元素
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
	pq->size--;
}

3.  其余操作

//队列元素个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}
//队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}
//取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);

	return pq->head->data;
}
//取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);

	return pq->tail->data;
}

4.  循环队列

在实际中还一种常用的队列叫做循环队列,可以用数组实现也能用链表实现,下面简单介绍一下用顺序存储结构实现的循环队列。

初始时:Q->front = Q->rear=0。
队首指针进1:Q->front = (Q->front + 1) % MAXSIZE。
队尾指针进1:Q->rear = (Q->rear + 1) % MAXSIZE。
队列长度:(Q->rear - Q->front + MAXSIZE) % MAXSIZE。

注意:在循环队列中,我们通常会舍去掉一个空间来表示空间已满,如果不这条rear就会和front指向同一个位置,这样不容易区分队满和队空。
队满条件: (Q->rear + 1)%Maxsize == Q->front
队空条件仍: Q->front == Q->rear
队列中元素的个数: (Q->rear - Q ->front + Maxsize)% Maxsize

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

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

相关文章

【Python报错已解决】`TypeError`:`TypeError: not enough arguments for format string`

🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 引言 在Python编程中,TypeError是一个常见的错误类型,它表示在操作或函数调用中使用了错误的类型。本文…

服务器死机/无故宕机排查思路/服务器起不来

1、查看服务器型号 dmidecode -t system dmidecode | grep ‘Product Name’ 2、风扇异响:查看BMC,坏了一个风扇其他的所有的风扇会全速转。 3、服务器亮红灯 红灯就是 故障告警 不一定是啥需要查看BMC口日志。这种就是看bmc日志 会有打印的 -问题现象&a…

java中的反射Reflection

Java中的反射(Reflection)是一种强大的机制,它允许程序在运行时查询和操作对象的类型信息。通过反射API,程序可以动态地创建对象、调用方法、访问字段和构造函数等,即使在编译时这些信息是未知的。 反射的原理 反射的…

微软分享其首款定制人工智能芯片Maia 100的更多细节

在2023年Ignite大会期间,微软首次宣布其已开发出名为Maia的自主人工智能加速器芯片。今年早些时候,在Build开发者大会上,微软分享了其首个自主研发的人工智能加速器Azure Maia 100的更多细节。Maia 100 是台积电 5nm 节点上制造的最大处理器之…

深拷贝与浅拷贝的区别

浅拷贝会导致深层数据改变,而深拷贝不会改变任何数据。 简单说就是: 浅拷贝只复制某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存。 深拷贝会创造一个一模一样的对象,新对象和原对象不共享内存&#x…

江协科技stm32————10-4 I2C通信协议

目录 I2C外设简介 I2C功能框图 基本结构图(一主多从) GPIO复用输入输出图 主机发送​编辑 START stop ​ EV5 (标志位) BTF 主机接收 ACK 软件/硬件波形对比 I2C外设简介 可变多主机模型11110作为10位地址模式的标志位…

「OC」初识MVC —— 简单学习UITableView的解耦

「OC」初识MVC —— 简单学习UITableView的解耦 文章目录 「OC」初识MVC —— 简单学习UITableView的解耦写在前面认识MVC解耦数据源代理 创建cell的基础类创建section的相关类分离数据源分离代理总结参考资料 写在前面 最近在学习了解MVC,然后开始发现&#xff0c…

VSCode连接SSH发生connection timeout

想用用实验室服务器连接vscode跑HM编码,今天突然连服务器连不到,报错:[13:47:13.068] Opening exec server for ssh-remotea406-server-lan [13:47:13.082] Initizing new exec server for ssh-remotea406-server-lan [13:47:13.089] Using c…

Apple LLM: 智能基础语言模型(AFM)

今天想和大家分享一下我最近在arXiv.org上看到苹果发表的一篇技术论文 Apple Intelligence Foundation Language Models (https://arxiv.org/abs/2407.21075),概述了他们的模型训练。这虽然出乎意料,但绝对是一个积极的惊喜! 这篇论文有那么多…

Dubbo如何传递链路追踪id?

1.什么是链路追踪? 分布式链路追踪就是将一次分布式请求还原成调用链路,将一次分布式请求的调用情况集中展示,比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等等。 链路跟踪主要功能: 故障快速定…

ET6框架(五)ECS组件式编程

文章目录 一、什么是ECS:二、ECS编程原则:三、组件生命周期: 一、什么是ECS: ECS即Entity、Component、System的简称,遵循组合优于继承的原则 Entity(实体) : 实体是一个概念,是指存在你游戏世界中的一个独立物体,是一…

图机器学习实战:从数据源到可视化

《动手学图机器学习》并不是一本纯粹介绍图机器学习理论的著作,Alessandro Negro 博士作为科学家和 Reco4 公司的 CEO,长期维护图数据源的推荐系统。他结合机器学习工程和图机器学习方法,通过推荐引擎、欺诈检测和知识图谱等案例,…

【书生2.4】InternLM + LlamaIndex RAG 实践

InternLM LlamaIndex RAG 实践 1 环境安装2 材料准备3 还原实验4 更换问题验证rag 【Intern Studio的gpu不足。本实验使用自有服务器】 1 环境安装 conda create -n llamaindex python3.10 conda activate llamaindex conda install pytorch2.0.1 torchvision0.15.2 torchau…

新160个crackme - 042-crackme

运行分析 提示需要key.dat文件 同目录下创建一个key.dat,提示key.dat内容不对 PE分析 C程序,32位,无壳 静态分析&动态调试 ida搜索字符串,找到关键字符串双击进入关键函数 对关键函数进行分析,注释如上当满足67行公…

信息安全数学基础(4)最大公因数

前言 在信息安全数学基础中,最大公因数(Greatest Common Divisor, GCD)是一个核心概念,它在密码学、数论等多个领域都有广泛应用。以下是对最大公因数的详细阐述: 一、定义 设a和b是两个非零整数,若整数d同…

论文阅读——Compact Single-Feed Dual-Mode Antenna for Active RFID Tag Application

文章目录 摘要一、天线设计A. 天线结构B. 天线演进 二、天线仿真与测试总结 论文来源:https://ieeexplore.ieee.org/document/7247651 摘要 文章提出了一种用于主动射频识别(RFID)标签应用的紧凑型单馈双模天线。该天线由一个位于FR4基板上的…

【读点论文】Scene Text Detection and Recognition: The Deep Learning Era

Scene Text Detection and Recognition: The Deep Learning Era Abstract 随着深度学习的兴起和发展,计算机视觉发生了巨大的变革和重塑。场景文本检测与识别作为计算机视觉领域的一个重要研究领域,不可避免地受到了这波革命的影响,从而进入…

ARM32开发——(二十一)ADC系统工作原理

1. ADC硬件结构 内部结构简化框图 2. ADC转换模式 2.1 单次转换,非扫描模式 2.2 连续转换,非扫描模式 2.3 单次转换,扫描模式 2.4 连续转换,扫描模式 3. 规则组和注入组 4. ADC数据对齐 5. ADC转换时间 总转换时间采样时间12 个 CK_ADC 周期 6. ADC内部校准 ADC有一个内置自…

前端速通面经八股系列(六)—— Vue(下)

接上ueue Vue下篇 四、路由1. Vue-Router 的懒加载如何实现2. 路由的hash和history模式的区别1. hash模式2. history模式3. 两种模式对比 3. 如何获取页面的hash变化4. $route 和$router 的区别5. 如何定义动态路由?如何获取传过来的动态参数?6. Vue-rou…

读书笔记:《深入理解Java虚拟机》(2)

Java内存区域与内存溢出异常 Java与C之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里 面的人却想出来。 对于从事C、C程序开发的开发人员来说,在内存管理领域,他们既是拥有最高权力的“皇帝”&#xf…