栈和队列详解

目录

    • 栈的概念及结构
    • 栈的实现
      • 数组栈的实现
      • 数组栈功能的实现
        • 栈的初始化void STInit(ST* pst)
          • 初始化情况一
          • 初始化情况二
        • 代码
        • 栈的插入void STPush(ST* pst, STDataType x)
          • 代码
        • 栈的删除void STPop(ST* pst)
          • 代码
        • 栈获取数据STDataType STTop(ST* pst)
          • 代码
        • 判断栈是否为空bool STEmpty(ST* pst)
        • 求栈的元素个数int STSize(ST* pst)
        • 栈的销毁void STDestory(ST* pst)
        • 栈的打印方式
        • 栈溢出问题
  • 队列
    • 队列的概念及结构
    • 队列的实现
      • 代码
    • 队列功能的实现
      • 队列的尾插void QueuePush(Queue*pq, QDataType x);
        • 结构体封装指针typedef struct Queue
          • 总结
        • 代码
      • 队列的头删void QueuePop(Queue* pq)
        • 代码
      • 队列的初始化void QueueInit(Queue* pq)
        • 代码
      • 队列的销毁void QueueDestroy(Queue* pq, QDataType x)
        • 代码
      • 取队尾数据QDataType QueueBack(Queue* pq)
        • 代码
      • 取队头数据QDataType QueueFront(Queue* pq)
        • 代码
      • 判断队列是否为空bool QueueEmpty(Queue* pq)
        • 代码
      • 获得队列的长度int QueueSize(Queue* pq)
        • 代码

感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒 个人主页
🥸🥸🥸 C语言
🐿️🐿️🐿️ C语言例题
🐣🐣🐣 python
🐓🐓🐓 数据结构C语言

栈的概念及结构

栈:一种特殊的线性表(数据是挨着储存,是连续的),其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。

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

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据也在栈顶。
在这里插入图片描述
在这里插入图片描述

注意栈顶不能叫做头,栈底不能叫做尾

栈的实现

栈分为两种
数组栈:数组通常让首元素的空间为栈底,末尾为栈顶(因为栈的规则是后进先出,数组让末尾为栈顶的尾删效率更高)
在这里插入图片描述

链式栈:
双向链表实现的话栈顶可以是头节点或者尾借点,用头节点当栈顶就是头插或者头删,尾节点当栈顶就是尾插尾删
单链表实现的话通常用头节点当栈顶,尾节点当栈底(因为单链表头插头删的时间复杂度低,而尾插尾删需要遍历一遍链表,时间复杂度高)
在这里插入图片描述

数组栈和链式栈相比,数组栈相对来说更好一点,因为数组栈简单快捷,虽然数组栈有时需要扩容,但是这种情况相对来说比较少,因为一次扩容就是2倍左右(不是规定必须2倍,只是2倍相对来说更合理)
对于两个链式栈相比,单链表会更合适一点,因为双向链表实现起来更复杂一点

数组栈的实现

typedef struct stack
{
	int* a;
	int top;//栈顶
	int capacity;空间
}ST;

数组栈功能的实现

栈的初始化void STInit(ST* pst)

初始化的时对于指针a和capacity,我们很容易就会想到a=NULL,capacity=0

而对于top的初始化就需要注意

初始化情况一

如果top为数组的下标,当top初始化为0时就会出现歧义
当数组为空时,top=0
当数组中有一个元素时,top也为0
为了避免这种情况,我们将top初始化成-1
在这里插入图片描述

在这里插入图片描述

初始化情况二

我们也可以认为top代表数据个数,或者理解成指向栈顶元素的下一个位置
这样top就可以初始化成0

在这里插入图片描述

代码
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->capacity = 0;
	pst->top = 0//或者pst->top = -1;
}

后面的代码我们讨论top初始化为0的情况

栈的插入void STPush(ST* pst, STDataType x)

栈的插入和之前我写的顺序表还有链表方式都差不多,有疑惑的可以去看看我之前写的文章

代码
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = realloc(pst->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}
栈的删除void STPop(ST* pst)

栈的删除要注意不能让top=0,因为top=0后就代表栈没有元素可以删除,所以要断言

代码
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}
栈获取数据STDataType STTop(ST* pst)

因为top初始化为0,所以top为栈顶元素的下一个的下标

代码
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	return pst->a[pst->top - 1];
}
判断栈是否为空bool STEmpty(ST* pst)
bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}
求栈的元素个数int STSize(ST* pst)
int STSize(ST* pst)
{
	return pst->top;
}
栈的销毁void STDestory(ST* pst)
void STDestory(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}
栈的打印方式

由于栈遵循后进先出,在访问栈的元素时,我们需要做到访问一个栈的元素就删除一个栈的元素,当栈访问完一遍后,栈的元素就全没了,也就是栈为空

int main()
{
	ST s;
	STInit(&s);
	STPush(&s, 1);
	STPush(&s, 2);
	STPush(&s, 3);
	STPush(&s, 4);
	STPush(&s, 5);
	while (!STEmpty(&s))

	{
		printf("%d ", STTop(&s));
		STPop(&s);
	}
	printf("\n");
	return 0;
}
栈溢出问题

栈有两种
数据结构的栈:存储数据

操作系统的栈:内存区域的划分(malloc,realloc…)

栈溢出中的栈是指操作系统的栈,发生的情况一般为递归出现返回条件错误,导致一直调用函数建立函数栈帧

队列

队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) (一端入一端出)

入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
在这里插入图片描述
队列的入队顺序和出队顺序是否和栈一样存在一对多的关系?
因为栈是后进先出,假设我们入栈一个数据A,在A入栈后不继续加入其他的数据B C D,那么如果我们进行出栈的话,数据A就会第一个出来
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以栈的入栈顺序和出栈顺序是一对多的情况,举个例子
栈的入栈顺序为A B C D E
则出栈顺序可能为 E D C B A,或者 A B C D E
对于E D C B A这种情况就是当数据全部入栈后,我们按照后进先出的规则,进行出栈,也就是E最后入栈,所以第一个出栈,D是倒数第二个入栈,所以出栈的时候就是第二个…

对于A B C D E这种情况就是当A刚入栈后,我们就让A直接出栈,因为其他的数据B C D E都还没有入栈,所以我们只能让A先出栈,之后再将B入栈,然后B再出栈…
出栈的顺序不止这两种,所以就不一一举例了,总之栈的出栈顺序和入栈顺序是一对多的关系

对于队列而言,因为是先进先出的顺序,所以入和出都只有一种情况,没有一对多的关系

队列的实现

队列也可以数组和链表的结构实现

如果用数组实现的就比较麻烦,因为队列是队尾入,对头出,那么如果用数组去实现队列,我们就要让数组头删,头删的复杂度为O(N)

如果用单链表的话,就很方便,因为是队尾入,队头出,删除数据不像栈一样要尾删,队列只需要让队头的数据删除就可以了,单链表的头删时间复杂度为O(1),并且实现起来要比双向链表要容易些,所以选择用单链表实现队列

单链表实现队列我们可以用带哨兵位的方式,也可以选择不用,带哨兵位需要malloc,最后还需要free掉哨兵位,并且哨兵位在这里的作用不是很大,所以队列的实现我们选择不带哨兵位的方式

实际中我们有时还会使用一种队列叫循环队列,环形队列可以使用数组实现,也可以使用循环链表实现

代码

typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* next;
}QNode;

队列功能的实现

队列的尾插void QueuePush(Queue*pq, QDataType x);

队列尾插函数如果只传两个参数参数头节点的地址,还有需要插入的数据x,(void QueuePush(QNode* * phead, QDataType x)),那么尾插的时间复杂度就为O(N),这样好像单链表就没什么优势了
如果我们传入三个参数void QueuePush(QNode* * phead, QNode* * ptail, QDataType x),其中ptail为指向尾节点的地址,虽然这样可以解决问题,但是我们要注意这里的指针全是二级指针,phead是plist指针传入的参数,因为plist是一个一级指针,要更改一级指针我们就需要将传参类型变成二级指针,ptail也是,非常麻烦

结构体封装指针typedef struct Queue

所以我们要创建一个结构体,将两个指针封装起来, 这样我们传入的参数就不用二级指针,只需要修改结构体成员就可以了

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size//size为队尾到队头总共有多少个节点
}Queue;
总结

如果遇到函数参数需要传入多个二级指针,那么我们可以用结构体将他们封装起来,结构体封装的成员就是二级指针的值,然后通过结构体访问成员将他们修改,并且在传参时我们只需要传入结构体的地址即可

代码
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode * )malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;
	if (pq->ptail == NULL)
	{
		pq->ptail = pq->phead = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

当ptail=NULL的时候就说明没有尾节点,只要队列有一个节点,那么尾节点都不可能为空,尾节点为空说明队列是空的,那么尾插就直接让尾节点=头节点=插入的newnode
如果尾节点不为空,那就直接让尾节点的next=newnode,然后插入newnode后newnode又是新的尾节点,所以让ptail=newnode
最后不要忘记size++

队列的头删void QueuePop(Queue* pq)

头删一般都得考虑两种情况,一种是只有一个节点,另一种就是没有节点

当没有节点时,我们assert断言就可以了

只有一个节点时,在头删后要小心ptail变成野指针,当phead走到下一个节点,发现是空后,如果直接把之前的头节点删除,我们会发现ptail还指向原来头节点的位置,free掉头节点后,ptail就成了野指针
在这里插入图片描述
在这里插入图片描述
所以需要判断如果phead走到下一个节点为空后,要让ptail赋值成NULL

代码
void QueuePop(Queue* pq)
{
	assert(pq);//结构体不能为空
	assert(pq->phead);//判断头节点为空的情况
	QNode* del = pq->phead;//del保存头节点方便后面释放空间
	pq->phead = pq->phead->next;
	free(del);
	if (pq->phead == NULL)
		pq->ptail == NULL;
		pq->size--
}

队列的初始化void QueueInit(Queue* pq)

代码
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

队列的销毁void QueueDestroy(Queue* pq, QDataType x)

代码
void QueueDestroy(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	size=0}

取队尾数据QDataType QueueBack(Queue* pq)

代码
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}

取队头数据QDataType QueueFront(Queue* pq)

代码
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	return pq->phead->val;
}

判断队列是否为空bool QueueEmpty(Queue* pq)

代码
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->phead == NULL;
}

获得队列的长度int QueueSize(Queue* pq)

代码
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

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

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

相关文章

裸金属服务器是什么

自推出裸金属服务器以来,它一直断断续续地出现在我们面前。最近,关于裸金属服务器、什么是裸金属服务器、裸金属服务器可以做什么、数据托架共享的讨论越来越多: 裸金属服务器(bare metal server,BMS)的官…

如何在OpenWRT上配置SFTP远程文件传输

如何在OpenWRT上配置SFTP远程文件传输 OpenWRT 是一款广泛使用的开源路由器固件,它能够让普通的家用路由器具备高级路由功能,提供更多自定义和优化选项。本文将介绍如何在OpenWRT上配置SFTP(SSH文件传输协议)服务,以便…

js生成不同的阅读数分配到每一篇上面,不会因为刷新而变动

js生成不同的阅读数分配到每一篇上面,不会因为刷新而变动 {%- for article in blog.articles -%}<div class"blog-articles__article article">{%- render article-card,article: article,media_height: section.settings.image_height,media_aspect_ratio: a…

面试遇到算法题:实现LRU缓存

请你设计并实现一个满足 LRU (最近最少使用) 缓存约束的数据结构。 这是一道大厂面试高频出现的算法题&#xff0c;难度为⭐️⭐️⭐️&#xff0c;属于中等&#xff0c;老铁们来一起看看这个题该怎么解&#xff1f; 1. 原题再现 没有废话&#xff0c;翠花&#xff0c;上酸菜&…

CountDownLatch使用错误+未最终断开连接导致线程池资源耗尽

错误描述&#xff1a; 我设置了CountDownLatch对线程的协作做出了一些限制&#xff0c;但是我发现运行一段时间以后便发现定时任务不运行了。 具体代码&#xff1a; public void sendToCertainWeb() throws IOException, InterruptedException {List<String> urlList …

HTML的学习-通过创建相册WEB学习HTML-第二部分

文章目录 二、学习开始3.6、form元素示例&#xff1a;添加form元素示例&#xff1a;action属性添加到form属性中 3.7、input元素示例&#xff1a;在input属性中添加参数 3.8、button元素示例&#xff1a;在button中添加type元素示例&#xff1a;定义单选按钮radio 3.9、id属性示…

交换式网络捕获网络流量的方法

交换式网络捕获网络流量的方法 参考资料&#xff1a; https://blog.csdn.net/weixin_44143678/article/details/107559329 # 一.端口镜像 端口镜像&#xff0c;又称为“端口监视”或“端口抄送”&#xff0c;是一种网络管理技术&#xff0c;旨在将网络设备上的特定端口的流…

伙伴匹配(后端)-- 数据库表设计

文章目录 用户表标签表队伍表用户队伍表sql语言分类&#xff08;题外话&#xff09;待更新... 在后端开发中&#xff0c;数据库表设计真的是非常重要的一环了&#xff0c;进入公司熟悉业务第一个要看的也是数据库的表,接下来就让我们看看本项目的数据库表有哪些吧&#xff08;暂…

LoRA: 大模型的低秩适配

笔记整理&#xff1a;陈一林&#xff0c;东南大学硕士&#xff0c;研究方向为不确定知识图谱规则学习 链接&#xff1a;https://arxiv.org/abs/2106.09685 1、动机 自然语言处理的一个重要范式包括在通用领域数据上进行大规模预训练&#xff0c;然后对特定任务或领域进行适应性…

Go语言中通过数据对齐降低内存消耗和提升性能

数据对齐是一种安排数据分配方式以加速 CPU 访问内存的方法。 不了解这个概念会导致额外的内存消耗甚至性能下降。 要了解数据对齐的工作原理&#xff0c;让我们首先讨论没有它会发生什么。假设我们分配两个变量&#xff0c;一个 int32 类型的 &#xff08;32 B&#xff09; 和…

【上海大学计算机组成原理实验报告】四、指令系统实验

一、实验目的 了解指令结构、PC寄存器的功能和指令系统的基本工作原理。 学习设计指令的方法。 二、实验原理 根据实验指导书的相关内容&#xff0c;对于部分使用频率很高&#xff0c;且只用几条微指令即可完成的简单操作&#xff0c;可以把这部分简单操作的微指令序列固定下…

安装esxi 7 对硬件资源的需求

安装VMware vSphere ESXi 7.0 虚拟化平台对硬件资源的基本需求如下&#xff1a; 处理器&#xff1a; 必须是64位x86架构的CPU。至少需要两个物理核心&#xff08;不过对于生产环境&#xff0c;建议更多的核心数以支持更多虚拟机并保证性能&#xff09;。支持并启用硬件辅助虚拟…

SpringMvc(2)RequestMapping注解

RequestMapping注解 1 、RequestMapping的作用2、RequestMapping的出现位置3、类上与方法上结合使用4、RequestMapping注解的value属性4.1 value属性的使用4.2 Ant风格的value4.3 value中的占位符&#xff08;重点&#xff09; 5、RequestMapping注解的method属性5.2衍生Mappin…

k8s集群CD工具-ArgoCD

ArgoCD是什么 Argo CD 是 Kubernetes 的声明式 GitOps 持续交付工具。应用程序定义、配置和环境应该是声明性的和版本控制的。应用程序部署和生命周期管理应该是自动化的、可审计的且易于理解。 官方文档 CD工作流&#xff08;无ArgoCD&#xff09; 假设有一个微服务应用程序…

<计算机网络自顶向下>

在计算机网络中&#xff0c;网络层包括数据平面和控制平面&#xff0c;它们分别负责网络数据转发和网络路由控制。以下是它们之间的区别&#xff1a; 数据平面&#xff08;Data Plane&#xff09;&#xff1a; 数据平面负责实际的数据传输和转发&#xff0c;它处理网络中的数据…

AI-数学-高中-40法向量求法

原作者视频&#xff1a;【空间向量】【考点精华】3法向量求法稳固&#xff08;基础&#xff09;_哔哩哔哩_bilibili 注意&#xff1a;法向量对长度没有限制&#xff0c;求法向量时&#xff0c;可以假设法向量z为任意一个取非0的值。 示例1&#xff1a; 示例2&#xff1a;

Transformer - 特征预处理

Transformer - 特征预处理 flyfish 原始数据 train_data.values [[ 5.827 2.009 1.599 0.462 4.203 1.34 30.531][ 5.76 2.076 1.492 0.426 4.264 1.401 30.46 ][ 5.76 1.942 1.492 0.391 4.234 1.31 30.038][ 5.76 1.942 1.492 0.426 4.234 1.31…

AndroidStudio中虚拟机(AVD)无法启动,出现unable to locate adb错误

1.检查Android SDK Platform-Tools是否安装(个人是通过这个方法解决的) 首先通过File-Project Structure-Project SDK检查SDK有没有被选中 步骤&#xff1a;打开file -> settings &#xff0c;搜索SDK 之后点击"-",在点击Apply进行安装 2.可能是驱动的问题 电脑…

牛客NC179 长度为 K 的重复字符子串【simple 哈希,滑动窗口 C++、Java、Go、PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/eced9a8a4b6c42b79c95ae5625e1d5fd 思路 哈希统计每个字符出现的次数。没在窗口内的字符要删除参考答案C class Solution {public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c…

python(一)

一、字面量 字面量:在代码中&#xff0c;被写下来的固定的值&#xff0c;称之为字面量。 Python中常用的有6种值(数据)的类型&#xff1a; 二、注释 注释的分类: 单行注释&#xff1a;以#开头&#xff0c;#右边的所有文字当作说明&#xff0c;而不是真正要执行的程序&#…