数据结构初阶---栈和队列

一、栈Stack

1.栈的概念

栈是一种特殊的线性表,只允许在固定的一端进行插入与删除操作。进行插入与删除操作的一端被称之为栈顶,另一端称之为栈底。栈中的数据元素遵循先进后出(FILO)或者说后进先出(LIFO)原则

栈的插入操作称之为压栈/入栈/进栈

栈的删除操作称之为出栈

栈的插入与删除操作均在栈顶进行。

注:先进后出FILO---Fisrt In Last Out ;后进先出LIFO---Last In First Out。

对于栈而言,规定只能从栈顶开始读取数据并删除数据。

对于栈而言,入栈顺序与出栈顺序是一对多。

关于栈的图解:

2.栈的实现

对于栈的实现,分为数组栈ArrayStack,链式栈LinkStack。链式栈的实现又可以分为双向链表与单链表栈的实现。

那么我们具体应该选择何种方式进行栈的实现呢?

其实三种都可以选择,但是考虑到缓存效率,数组栈是最优的,物理上连续给予了数组栈较高的缓存效率。我们也可以实现链式栈。

使用链式结构主要是在结构的构建上比较复杂,在后续的接口实现上比起数组要相对简单,但是数组结构的构建要简单一些,因此其实难度大差不差。

那么我们以数组栈为例:

3.数组栈AStack

结构如下:

#define AStackDataType int
typedef struct ArrayStack//数组栈
{
	AStackDataType* a;
	int top;//栈顶
	int capacity;//空间容量
}AS;

接口函数

①初始化AStackInit

void AStackInit(AS* pas); 

对于栈而言,初始化依然可以选择开辟空间或者不开辟空间,如果这里不开辟空间,而是置空以及0,那么在插入前的容量检查接口中,我们就得判断空间然后扩容。

//初始化
void AStackInit(AS* pas)
{
	assert(pas);
	//还是先不开辟空间
	pas->capacity = 0;
	pas->top = 0;//top如果初始化0--->top最后指向栈顶后一个位置,类似数组长度size
	//pas->top = -1;//top初始化-1--->top最终指向栈顶、类似下标了
	pas->a = NULL;
}

注意top初始化的值:

[i] 如果top初始化为0,第一个数据插入则top为1,那么top作为下标而言,就是栈顶的下一个位置;作为值而言,就是栈的数据个数。栈顶元素下标为 top-1。

[ii] 如果top初始化为-1,第一个数据插入则top为0,那么top作为下标就是栈顶元素下标。

②容量检查AStackCheckMemory

只在插入接口中用得上,也可以直接写到插入接口内。  

void AStackCheckMemory(AS* pas); 

top初始化为0时,代表了top的值就是栈中数据的个数 ,那么如果top == capacity,说明了空间不够了,需要扩容,扩容又需要考虑第一次capacity为0的情况,那么使用一个三目操作符即可,其实与前面顺序表的扩容操作是一样的。

void AStackCheckMemory(AS* pas)
{
	if (pas->capacity == pas->top)
	{
		int NewCapacity = pas->capacity == 0 ? 4 : pas->capacity * 2;
		AStackDataType* tmp = (AStackDataType*)realloc(pas->a, sizeof(AStackDataType) * NewCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		pas->a = tmp;
		pas->capacity = NewCapacity;
	}
}
③销毁栈AStackDestroy

void AStackDestroy(AS* pas); 

对于栈的销毁,直接释放数组a的空间即可,然后top、capacity置0,a置空。 

//销毁栈
void AStackDestroy(AS* pas)
{
	assert(pas);
	free(pas->a);
	pas->a = NULL;
	pas->capacity = 0;
	pas->top = 0;
}
④栈顶插入AStackPush

void AStackPush(AS* pas, AStackDataType x); 

对于数组栈而言,栈顶插入数据,其实就是尾插,数组的尾插就非常简单了,直接访问尾部(栈顶)然后在top位置插入数据,注意top如果初始化0,那么top始终指向栈顶的下一个位置,所以插入后别忘记top自增1。

//插入
void AStackPush(AS* pas, AStackDataType x)
{
	assert(pas);
	AStackCheckMemory(pas);
	pas->a[pas->top] = x;
	pas->top++;
}
⑤栈顶删除AStackPop

void AStackPop(AS* pas); 

栈中有效元素是从栈底(数组首元素)开始,到栈顶(top-1),那么我们只需要将top自减1,那么对于栈顶而言,就向前移了一步,即完成了栈顶的删除,也不需要对原来栈顶的数据进行修改。

唯一需要注意的就是栈内没有数据时需要进行断言,不能进行删除操作,只有一个数据没有问题。

//删除
void AStackPop(AS* pas)
{
	assert(pas);
	assert(pas->top > 0);//栈内无数据不能删除
	pas->top--;
}
⑥获取栈顶元素AStackTop

AStackDataType AStackTop(AS* pas); 

我们使用数组实现栈,好处其一就是下标的随机访问很方便,获取栈顶元素,那么直接访问top-1处的元素即可,同时我们要对-1这些字眼很敏感,当top为0时栈内无数据啊a[top-1]越界,需要断言,top为1时没有问题。

//获取栈顶元素
AStackDataType AStackTop(AS* pas)
{
	assert(pas);
	assert(pas->top > 0);//栈内无数据不能获取
	return pas->a[pas->top-1];
}
⑦获取栈有效元素个数AStackSize

int AStackSize(AS* pas); 

 我们将top初始化为0的好处在这个接口上也体现出来了,那么有效元素个数就是top的值。

//获取栈中有效元素个数
int AStackSize(AS* pas)
{
	assert(pas);
	return pas->top;
}
⑧检测栈是否为空AStackEmpty

bool AStackEmpty(AS* pas);

检测栈是否为空,为空返回true、不为空返回false。

top为0代表栈为空,top不为0代表栈不为空:

//检测栈是否为空,为空返回true,不为空返回false
bool AStackEmpty(AS* pas)
{
	assert(pas);
	return pas->top == 0;
	//if (pas->top == 0)
	//	return true;
	//else
	//	return false;
}

4.Extra---数据结构与操作系统的栈

数据结构的栈与操作系统(OS)的栈不是一个栈,数据结构的栈是一种数据的存储形式;操作系统中的栈是内存中的一块区域,里面存放临时变量以及函数的形式参数等。

栈溢出中的栈指的是内存(操作系统)中的栈,栈溢出指的是因为某些情况如函数递归返回条件出现问题导致栈的使用空间一直增加且无法回收,那么就会导致栈溢出。

二、队列Queue

1.队列的概念

只允许在一端进行数据的插入,在另一端进行数据的删除操作的特殊线性表,具有先进先出(FIFO)的原则。

入队--->在队尾插入数据

出队--->在队头删除数据

数据从队尾进入,最先进入的数据就会处于队头,最先出队(删除)。

对于队列而言,只能在队头读取数据并且删除。

对于队列而言,入队顺序与出队顺序始终是一对一。

如入队12345678---出队12345678。

2.队列的实现

同栈一样,可以使用数组、链表来实现队列:

数组实现队列,如果我们头插,那么会面临着数据的挪位,如果我们尾插,那么尾插虽然方便,但是就会面临到头删的问题,同样也会面临挪位的问题,但是也可以解决,比如说不通过挪位,而是挪动队头下标front和队尾下标back来解决。

链表实现队列,单链表头插就得尾删,尾插就得头删,也不是很方便,但是同样可以解决,如我们再定义一个尾指针tail,由于尾插不需要尾删,因此一个尾指针就能够满足尾插(如果尾删还需要尾结点前一个结点)。这样的话我们选择尾插和头删。

由于栈我们使用了数组实现,那么队列我们就选择单链表实现。

3.链式队列LinkQueue

结构:

#define QueueDataType int 
typedef struct QueueNode
{
	QueueDataType val;
	struct QueueNode* next;
}QNode;

为了便于后面函数传参的简洁,我们再定义一个结构体,成员为头尾指针phead与ptail,同时定义一个size用于记录队列数据个数:

typedef struct Queue
{
	QNode* phead;//头
	QNode* ptail;//尾
	int size;//记录队列数据个数
}Queue;

接口函数

①初始化QueueInit

void QueueInit(Queue* pq); 

pq指针调用成员头指针、尾指针和数据个数,将头指针phead与尾指针ptail置空,数据个数size置为0。

//初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}
②销毁QueueDestroy

void QueueDestroy(Queue* pq); 

在插入接口中,我们对链表进行了空间的开辟,那么需要释放开辟的空间,定义一个QNode*的指针cur,遍历释放空间,注意记录下一个结点的位置。释放完对phead、ptail置NULL,size置0。

//销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		//pq->phead = pq->phead->next;//不直观
		free(cur);
		cur = next;
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

与前面的链表的销毁相同。 

③队尾插入QueuePush

void QueuePush(Queue* pq, QueueDataType x); 

在队尾插入,就是在尾结点ptail后插入,分为两步,首先我们创建一个结点newnode,将结点newnode的next指向NULL,x值赋给newnode的val。然后将ptail->next指向该结点,同时将ptail往后移。但是,队列中没有结点时,就不能ptail->next了,要直接将newnode赋给phead与ptail。

//插入
void QueuePush(Queue* pq, QueueDataType x)
{
	assert(pq);
	//创建插入的结点newnode
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->next = NULL;
	newnode->val = x;

	//插入
	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

最后由于插入了一个数据,不要忘记将size自增1。 

④队头删除QueuePop

void QueuePop(Queue* pq); 

空队列的时候进行断言,不能进行删除。

删除队头结点,将phead指向后一个结点,释放原来的队头结点。当队列只有一个数据,也就是链表只有一个结点时,我们不仅需要将phead指向下一个结点(其实就是NULL),同时要将ptail指向NULL,不然就会导致ptail野指针,指向了一块释放掉了的空间。

//删除
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->phead);//无结点
	QNode* cur = pq->phead;
	QNode* next = cur->next;
	free(cur);
	cur = NULL;
	pq->phead = next;
	if (pq->phead == NULL)
		pq->ptail = NULL;

	pq->size--;
}

最后不要忘记size自减1。

⑤获取队头元素QueueFront

QueueDataType QueueFront(Queue* pq); 

队头元素,phead指向的结点的val值即是队头元素。同时队列不能为空,进行断言。

//获取队列头部元素
QueueDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);//队列不为空

	return pq->phead->val;
}
⑥获取队尾元素QueueBack

QueueDataType QueueBack(Queue* pq); 

队尾元素,ptail指向的结点中的val值即为队尾元素。同样队列不能为空,断言。

//获取队列尾部元素
QueueDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->phead);//断言pq->phead或者pq->ptail都是一样的

	return pq->ptail->val;
}
⑦获取队列有效元素个数QueueSize

int QueueSize(Queue* pq); 

前面Queue结构体中我们创建了一个成员变量size记录了数据个数,这里直接返回。可以是空队列。

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

	return pq->size;
}
⑧检测队列是否为空QueueEmpty

bool QueueEmpty(Queue* pq);

为空返回true,不为空返回false。 

这个可以用phead或者ptail或者size判断,phead或者ptail为空、size为0,那么就是空队列,反之不是空队列。

//检测队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->phead == NULL;
}

 有关栈与队列的三个选择题:

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

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

相关文章

ASP.NET Web UI 框架 Razor Pages/MVC/Web API/Blazor

前言 💢 🎯 记得以前用 Asp.net 做 网页开发 是的时候那时候还是 Web Forms ,后来 MVC 出来后也是火的不得了。那个时候还没有所谓的前后端分离一说,像 Vue.js、React 、Angular 的这些前端框架还没出生,那时候 Jquery…

YOLOV11 快速使用教程

概述 这里主要记录使用NVIDIA GPU pytorch 检测系列模型的快速使用方式,可以快速解决一些工业应用的问题,比如:无网、数据大需要改路径、需要记录不同实验结果等问题。 安装 参考官网,自己安装好Python > 3.8和pytorch >…

双绞线直连两台电脑的方法及遇到的问题

文章目录 前言一、步骤二、问题总结:问题1:遇到ping不通的问题。问题2:访问其他电脑上的共享文件时提示输入网络凭证问题3:局域网共享文件时提示“没有权限访问,请与网络管理员联系请求访问权限” 前言 办公室里有两台电脑,一台装了显卡用于…

电子信息工程自动化 基于单片机的出租车计价器设计

摘 要 出租车作为一种城市中非常重要的公共交通工具,他与人们的生活息息相关。所以我也设计了一款出租车计价器,它采用模块化设计,包含里程测量模块、数据存储模块、按键模块、时钟模块、显示模块、语音播报模块六大主要模块。本设计的出租车…

day1:ansible

ansible-doc <module_name>&#xff08;如果没有网&#xff0c;那这个超级有用&#xff09; 这个很有用&#xff0c;用来查单个模块的文档。 ansible-doc -l 列出所有模块 ansible-doc -s <module_name> 查看更详细的模块文档。 ansible-doc --help 使用 --help …

yolov,coco标记的无增强版的水稻病害数据集 共 1448 张图片

之前用过增强图片之后标记的效果不是特别理想&#xff0c;因此这里给大家分享一下使用无增强的版本&#xff1a; yolov,coco标记的无增强版的水稻病害数据集 共 1448 张图片 稻瘟病 细菌性枯萎病 褐斑病 数据集下载&#xff1a; yolov8&#xff1a;https://download.csdn.net…

uniapp微信小程序开发地图多边形渲染,圆形渲染,省市区区域渲染解决方案-(已实测通过)

一、多边形渲染(只需给map组件绑定对应的polygons即可) <mapid="map"class="map":latitude="latitude":longitude="longitude":markers="covers":polyline="polyline":scale="18"@markertap="…

IntelliJ+SpringBoot项目实战(28)--整合Beetl模板框架

在前面的文章里介绍过freemarker&#xff0c;thymeleaf模板引擎&#xff0c;本文介绍另一个性能超高的模板引擎---Beetl&#xff0c;据说此模板引擎的性能远超Freemarker。官网的说法是&#xff0c;Beetl 远超过主流java模板引擎性能(引擎性能5-6倍于FreeMarker&#xff0c;2倍…

jeecg-uniapp 跨域问题解决方法记录

今天折腾这个很恶心的问题,工作需要经验才行,根本没有什么技术难点,都是经验而已 问题在此 发现没有替换掉前缀 :8085/#/pages/login/login:1 Access to XMLHttpRequest at http://192.168.152.32:8194/h5api/api/user/login from origin http://localhost:8085 has been bloc…

AD20 原理图库更新到原理图

一 点击工具&#xff0c;从库更新。快捷键TL 二 点击完成 三 执行变更&#xff0c;最后点击关闭

linux基于systemd自启守护进程 systemctl自定义服务傻瓜式教程

系统服务 书接上文: linux自启任务详解 演示系统:ubuntu 20.04 开发部署项目的时候常常有这样的场景: 业务功能以后台服务的形式提供,部署完成后可以随着系统的重启而自动启动;服务异常挂掉后可以再次拉起 这个功能在ubuntu系统中通常由systemd提供 如果仅仅需要达成上述的场…

Unity 设计模式-策略模式(Strategy Pattern)详解

策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;定义了一系列算法&#xff0c;并将每种算法封装到独立的类中&#xff0c;使得它们可以互相替换。策略模式让算法可以在不影响客户端的情况下独立变化&#xff0c;客户端通过与这些策略对象进…

Android环境搭建

Android环境搭建 第一步&#xff1a;安装 Homebrew 执行以下命令来安装 Homebrew&#xff1a; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"检测是否安装成功&#xff1a; brew --version第二步&#xff1a;安装 No…

摄影后期学什么_好学吗?

当你按下相机快门&#xff0c;捕捉到那珍贵的瞬间&#xff0c;摄影可还没画上句号哦&#xff01;摄影后期就像是一场神奇的魔法秀&#xff0c;能让你的照片从平凡瞬间变身惊艳大片。那在这场魔法之旅中&#xff0c;咱们得学习哪些厉害的法术呢&#xff1f; 先来说说光影调整这…

短视频矩阵系统saas源码 ---技术源头搭建部署

短视频矩阵系统源码 短视频矩阵系统源码主要有三种框架&#xff1a;Spring、Struts和Hibernate。Spring框架是一个全栈式的Java应用程序开发框架&#xff0c;提供了IOC容器、AOP、事务管理等功能。Struts框架是一个MVC架构的Web应用程序框架&#xff0c;用于将数据模型、Web应用…

mock.js介绍

mock.js http://mockjs.com/ 1、mock的介绍 *** 生成随机数据&#xff0c;拦截 Ajax 请求。** 通过随机数据&#xff0c;模拟各种场景&#xff1b;不需要修改既有代码&#xff0c;就可以拦截 Ajax 请求&#xff0c;返回模拟的响应数据&#xff1b;支持生成随机的文本、数字…

Spring Authorization Server入门 (十二) 实现授权码模式使用前后端分离的登录页面

基于Spring Session的前后端分离文章已发布&#xff1a;《Spring Authorization Server基于Spring Session的前后端分离实现》 2023-12-01修改&#xff1a;在session-data-redis(Github)分支中添加了基于spring-session-data-redis的实现&#xff0c;无需借助nonceId来保持认证…

Codeforces Round 991 (Div. 3) F. Maximum modulo equality(区间gcd模板)

思路&#xff1a;我们由题意可以知道我们只需要维护区间gcd即可&#xff0c;因为差分一下后&#xff0c;维护的差分数组的区间gcd即为原数组所要求的值 #include<bits/stdc.h>using namespace std;typedef long long ll; typedef pair<ll, ll>PII; const int N 2…

智创 AI 新视界 -- 优化 AI 模型训练效率的策略与技巧(16 - 1)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

基于 NXP S32K312+FS23 的汽车通用评估板方案

S32K3 系列是 NXP 推出的面向汽车电子和工业应用的微控制器&#xff0c;基于 ARMCortex-M7 内核&#xff0c;支持单核、双核和锁步内核配置。S32K3 系列具有内核、内存和外设数量方面的可扩展性&#xff0c;符合 ISO26262 标准&#xff0c;能达到 ASIL B/D 安全等级&#xff0c…