【数据结构】栈和队列的模拟实现

前言:前面我们学习了单链表并且模拟了它的实现,今天我们来进一步学习,来学习栈和队列吧!一起加油各位,后面的路只会越来越难走需要我们一步一个脚印!

💖 博主CSDN主页:卫卫卫的个人主页 💞
👉 专栏分类:数据结构 👈
💯代码仓库:卫卫周大胖的学习日记💫
💪关注博主和博主一起学习!一起努力!
在这里插入图片描述


什么是栈

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出的原则(如下图所示)。
在这里插入图片描述
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。


如何实现栈

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。所以我们今天通过使用动态数组来模拟实现栈。


栈的基本结构

我们已经确定了通过动态数组来模拟实现栈,那么我们要实现哪些功能呢?

  1. 栈的初始化
  2. 栈的销毁
  3. 入栈
  4. 出栈
  5. 获取栈顶元素
  6. 检测栈是否为空
  7. 获取栈中有效元素个数

首先我们需要考虑如何记录栈顶元素的位置,我们用top来记录,但是top是不是应该是0还是什么数据呢?如果top指向的是0那么我top是0的时候栈中到底是一个元素还是没有元素,这是十分具有歧义的,所以我们这里用负一来表示top当有元素进入时就让top加加即可(如下图)。
在这里插入图片描述

具体结构如下:

typedef struct Stack
{STDataType* a;int top; //标识顶部int capacity;
}ST;

栈的基本功能

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>typedef int STDataType;typedef struct Stack
{STDataType* a;int top; //标识顶部int capacity;
}ST;//栈的初始化
void STInit(ST* pst);//栈的销毁
void STDestroy(ST* pst);//栈顶插入删除//入栈
void STPush(ST* pst, STDataType x);//出栈
void STPop(ST* pst);// 获取栈顶元素
STDataType STTop(ST* pst);// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool STEmpty(ST* pst);// 获取栈中有效元素个数
int STSize(ST* pst);

栈的初始化

思路导读:前面我们讲到过用top来记录栈顶的位置,之所以让它从负一开始就是为了方便我们访问栈顶数据,也是为了防止歧义,如下图所示。我们初始化也只需要将开辟的空间指向空指针,空间容量为0即可。在这里插入图片描述代码实现:

void STInit(ST* pst)//栈的初始化
{assert(pst);pst->top = -1;//表示top指向栈顶元素的下一个位置pst->a = NULL;pst->capacity = 0;
}

栈的销毁

思路导读:我们只需要找到a开辟的空间将它释放掉并将他置为空指针即可,然后其它值Top处理为-1,空间容量置为0即可
代码实现

void STDestroy(ST* pst)//栈的销毁
{assert(pst);free(pst->a);pst->a = NULL;pst->top = -1;pst->capacity = 0;
}

入栈

思路导读:我们入栈首先就是得开辟一块空间存放即将要入栈的值,然后将这块空间传给a即可,然后再把栈顶元素赋值,即可完成入栈。(如果这块地方有不懂的友友可以去看我之前的博客有详细讲解动态顺序表)
代码实现:

void STPush(ST* pst, STDataType x)//入栈
{assert(pst);if (pst->top + 1 == pst->capacity)//判断是否有空间{int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);//防止开辟失败俩个空间都丢失if (tmp == NULL){perror("realloc fail");return;}pst->a = tmp;pst->capacity = newcapacity;}pst->top++;//top加一pst->a[pst->top] = x;//栈顶赋值
}

出栈

思路导读:关于出栈,我们要知道他是先进后出,在前面的图中我们都有详细的讲解,出去一个元素我们让top往下走一个即可。
代码实现

void STPop(ST* pst)//出栈
{assert(pst);//判断是不是空assert(pst->top >= 0);//判断有没有元素pst->top--;//top减减即可
}

获取栈顶元素

思路导读:首先我们还是得判断传来的数据是否为空,里面是否有元素,然后我们直接访问栈顶元素即可。

STDataType STTop(ST* pst)// 获取栈顶元素
{assert(pst);assert(pst->top >= 0);return pst->a[pst->top];
}

函数测试与效果图:

void Test2()
{ST sl;STInit(&sl);STPush(&sl, 0);printf("%d ", STTop(&sl));STPush(&sl, 9);printf("%d ", STTop(&sl));STPush(&sl, 2);printf("%d ", STTop(&sl));STPush(&sl, 2);printf("%d ", STTop(&sl));printf("上面是入栈后\n");printf("下面是出栈后栈顶元素\n");STPop(&sl);printf("%d ", STTop(&sl));STPop(&sl);printf("%d ", STTop(&sl));STPop(&sl);printf("%d ", STTop(&sl));STPop(&sl);
}

在这里插入图片描述


检测栈是否为空

思路导读:首先判断传来的是否有这个结构体,我们只需要判断top是否是负一如果是说明这个为真则返回0,不是则返回非0的结果
代码实现:

bool STEmpty(ST* pst)// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
{assert(pst);return pst->top == -1;//如果是-1则是空返回true,如果不是返回false
}

获取栈中有效元素个数

思路导读:我们只需要将top加一即为元素个数,因为top指向的栈顶元素。

int STSize(ST* pst)// 获取栈中有效元素个数
{assert(pst);return pst->top + 1;
}

整体函数测试

void Test3()
{ST sl;STInit(&sl);STPush(&sl, 1);STPush(&sl, 3);STPush(&sl, 0);STPush(&sl, 9);STPush(&sl, 2);STPush(&sl, 8);printf("入栈后元素的个数:%d\n", STSize(&sl));printf("下面是入栈元素\n");while (!STEmpty(&sl)){	printf("%d ", STTop(&sl));STPop(&sl);//出栈}STDestroy(&sl);
}
int main()
{//Test1();//Test2();Test3();return 0;
}

具体效果图:
在这里插入图片描述
到这里关于栈的实现我们都已经讲完了,接下来我们开始队列的学习!


什么是队列

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出(我们可以通俗的理解成一头进一头出,先进去的先出去)。
入队列:进行插入操作的一端称为队尾。
出队列:进行删除操作的一端称为队头。
具体如下图
在这里插入图片描述


如何实现队列

由于队列的特殊性,只能一头进一头出,且先进的先出,在实现它的时候我们只需要考虑尾插和头删即可,在尾插和头删上效率较高的应该就是单链表了,但我们应当用一个tail的尾指针来记录,防止尾增的时候再次遍历链表。故我们通过单链表来实现队列。


队列的基本功能与结构

关于下面为什么会在用一个结构体来记录头指针和尾指针,是因为如果我们补定义一个结构体来记录,我们就会在入队列和出队列的时候,需要传一个二级指针来修改队列中的值,为了防止这样的麻烦我们就直接定义一个结构体来记录即可。


typedef int QDataType;typedef struct QueueNode
{QDataType val;struct QueueNode* next;
}QNode;typedef struct Queue//记录头指针和尾指针
{QNode* phead;QNode* ptail;int size;//记录元素个数
}Queue;// 初始化队列
void QueueInit(Queue* pq);//销毁队列
void QueueDestory(Queue* pq);// 队尾入队列
void QueuePush(Queue* pq, QDataType x);// 队头出队列
void QueuePop(Queue* pq);// 获取队列头部元素
QDataType QueueFront(Queue* pq);// 获取队列队尾元素
QDataType QueueBack(Queue* pq);// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* pq);// 获取队列中有效元素个数
int QueueSize(Queue* pq);

队列的初始化

思路导读:关于队列的初始化,我们只需要将头指针和尾指针置为空即可,然后将里面的元素置为0。
代码实现:

void QueueInit(Queue* pq)//队列的初始化
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}

队尾入队列

思路导读:首先我们得分两种情况:一种是该队列为空第一次入队列,一种是该队列不为空不是第一次入队列,但无论如何都要开辟一块空间存放新开辟的值。当为第一次入队列的时候,我们只需要将头节点和尾节点全部指向这块新开辟的空间即可。当不是第一次入的时候,我们需要找到尾结点将它指向新开辟的这块空间即可完成入队列,且每当进入一个元素我们让size加加即可知道队列中的元素个数。如下图所示:
在这里插入图片描述
代码实现:

void QueuePush(Queue* pq, QDataType x)//队尾入队列
{assert(pq);QNode* newnode = malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");return;}newnode->val = x;newnode->next = NULL;//判断是不是第一次if (pq->ptail == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;}pq->size++;//记录元素}

队头出队列

思路导读:首先我们得分为两种情况,一种是队列中只有一个元素的情况,一种是队列中有多个元素的情况。当队列中有多个元素的时候,我们需要一个指针来保留头节点,并找到头节点的下个位置让它成为新的节点,并将原来的头节点释放掉即可完成出队列。但当队列中只有一个元素时候,我们需要将头节点释放的同时也要将尾节点置为空指针,不然就会出现野指针的情况。
代码实现

void QueuePop(Queue* pq)// 队头出队列
{assert(pq);assert(pq->phead);Queue* tmp = pq->phead;//记录头指针,方便释放空间pq->phead = pq->phead->next;//让头指针指向下一个元素free(tmp);//释放头指针if (pq->phead == NULL)//如果队列中只有一个元素,将尾指针也置为空指针{pq->ptail = NULL;}pq->size--;
}

获取队列头部元素

思路导读:我们只需要找到头节点指向的val的值将它返回即可。
代码实现

QDataType QueueFront(Queue* pq)// 获取队列头部元素
{assert(pq);assert(pq->phead);assert(pq->ptail);return pq->phead->val;
}

获取队列队尾元素

思路导读:我们在这之前一直记录了一个尾部节点,因此获取队尾元素的时候我们不需要重新在遍历队列,只需要返回尾节点的值即可。
代码实现

QDataType QueueBack(Queue* pq)// 获取队列队尾元素
{assert(pq);assert(pq->phead);assert(pq->ptail);return pq->ptail->val;
}

检测队列是否为空

思路导读:我们只需要判断之前存储数据个数的size是否为空即可。
代码实现

bool QueueEmpty(Queue* pq)// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
{assert(pq);return pq->size == 0;
}

获取队列中有效元素个数

思路导读:我们直接返回记录元素个数的size即可获取队列中有效元素的个数。

int QueueSize(Queue* pq)// 获取队列中有效元素个数
{assert(pq);return pq->size;
}

销毁队列

思路导读:我们要把队列中的每一个结点都释放掉,我们需要一个指针dl记录头指针原本的位置,让头指针往后走遍历队列依次释放经过的位置,具体如下图
在这里插入图片描述
代码实现:

void QueueDestory(Queue* pq)//销毁队列
{assert(pq);Queue* dl = NULL;while (pq->phead){dl = pq->phead;pq->phead = pq->phead->next;free(dl);}pq->ptail = NULL;pq->size = 0;pq->phead = NULL;
}

整体函数测试

void Test2()
{Queue s;QueueInit(&s);QueuePush(&s, 1);QueuePush(&s, 2);QueuePush(&s, 3);QueuePush(&s, 4);QueuePush(&s, 5);printf("%d ", QueueFront(&s));		printf("%d ", QueueBack(&s));printf("%d\n", QueueSize(&s));QueuePop(&s);QueuePop(&s);printf("%d ", QueueFront(&s));printf("%d\n", QueueSize(&s));if (!QueueEmpty(&s)){QueuePop(&s);printf("%d ", QueueFront(&s));printf("%d\n", QueueSize(&s));}printf("%d\n", QueueSize(&s));QueueDestory(&s);
}int main()
{//Test1();Test2();return 0;
}

运行结果:在这里插入图片描述


整体代码

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>typedef int QDataType;// 队列的结构
typedef struct QueueNode
{QDataType val;struct QueueNode* next;
}QNode;typedef struct Queue//记录头指针和尾指针
{QNode* phead;QNode* ptail;int size;//记录元素个数
}Queue;// 初始化队列
void QueueInit(Queue* pq);//销毁队列
void QueueDestory(Queue* pq);// 队尾入队列
void QueuePush(Queue* pq, QDataType x);// 队头出队列
void QueuePop(Queue* pq);// 获取队列头部元素
QDataType QueueFront(Queue* pq);// 获取队列队尾元素
QDataType QueueBack(Queue* pq);// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* pq);// 获取队列中有效元素个数
int QueueSize(Queue* pq);
#include"Queen.h"void QueueInit(Queue* pq)//队列的初始化
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}void QueuePush(Queue* pq, QDataType x)//队尾入队列
{assert(pq);QNode* newnode = malloc(sizeof(QNode));//开辟新空间if (newnode == NULL){perror("malloc fail");return;}//赋值newnode->val = x;newnode->next = NULL;//判断是不是第一次if (pq->ptail == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;}pq->size++;//记录元素}void QueuePop(Queue* pq)// 队头出队列
{assert(pq);assert(pq->phead);Queue* tmp = pq->phead;//记录头指针,方便释放空间pq->phead = pq->phead->next;//让头指针指向下一个元素free(tmp);//释放头指针if (pq->phead == NULL)//如果队列中只有一个元素,将尾指针也置为空指针{pq->ptail = NULL;}pq->size--;
}QDataType QueueFront(Queue* pq)// 获取队列头部元素
{assert(pq);assert(pq->phead);assert(pq->ptail);return pq->phead->val;
}QDataType QueueBack(Queue* pq)// 获取队列队尾元素
{assert(pq);assert(pq->phead);assert(pq->ptail);return pq->ptail->val;
}bool QueueEmpty(Queue* pq)// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
{assert(pq);return pq->size == 0;
}int QueueSize(Queue* pq)// 获取队列中有效元素个数
{assert(pq);return pq->size;
}void QueueDestory(Queue* pq)//销毁队列
{assert(pq);Queue* dl = NULL;while (pq->phead){dl = pq->phead;pq->phead = pq->phead->next;free(dl);}pq->ptail = NULL;pq->size = 0;pq->phead = NULL;
}#define _CRT_SECURE_NO_WARNINGS 1
#include"Queen.h"void Test1()
{Queue sl;QueueInit(&sl);QueuePush(&sl, 1);//printf("%d\n", QueueFront(&sl));QueuePush(&sl, 9);//printf("%d\n",QueueFront(&sl));//QueuePop(&sl);QueuePush(&sl, 2);QueuePush(&sl, 10);printf("%d\n", QueueFront(&sl));printf("%d\n",QueueBack(&sl));
}void Test2()
{Queue s;QueueInit(&s);QueuePush(&s, 1);QueuePush(&s, 2);QueuePush(&s, 3);QueuePush(&s, 4);QueuePush(&s, 5);printf("%d ", QueueFront(&s));		printf("%d ", QueueBack(&s));printf("%d\n", QueueSize(&s));QueuePop(&s);QueuePop(&s);printf("%d ", QueueFront(&s));printf("%d\n", QueueSize(&s));if (!QueueEmpty(&s)){QueuePop(&s);printf("%d ", QueueFront(&s));printf("%d\n", QueueSize(&s));}printf("%d\n", QueueSize(&s));QueueDestory(&s);
}int main()
{//Test1();Test2();return 0;
}

结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。


🫵🫵🫵 祝各位接下来好运连连 💞

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

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

相关文章

【ArcGIS Pro微课1000例】0034:矢量数据几何校正案例(Spatial Adjustment)

本案例讲解矢量数据几何校正&#xff0c;根据一个矢量数据去校正另外一个矢量数据。 文章目录 一、加载实验数据二、空间校正三、注意事项 一、加载实验数据 在ArcGIS Pro中加载数据效果如下&#xff1a; design&#xff1a;需要校正的数据图层planroadcenter&#xff1a;目标…

ubuntu22.04安装网易云音乐

附件&#xff1a; https://download.csdn.net/download/weixin_44503976/88557248 wget https://d1.music.126.net/dmusic/netease-cloud-music_1.2.1_amd64_ubuntu_20190428.deb wget -O patch.c https://aur.archlinux.org/cgit/aur.git/plain/patch.c?hnetease-cloud-m…

【Docker】从零开始:5.Docker安装与卸载

【Docker】从零开始&#xff1a;4.Docker安装 安装步骤1.确定你是CentOS7及以上版本2.卸载旧版本a.查看是否已安装dockerb.如何安装了卸载docker 3.通过yum安装gcc相关依赖包4.安装需要的软件包5.设置yum stable镜像仓库 &#xff08;源&#xff09;1.备份源2.配置源阿里云仓库…

数据库的基本概念以及MySQL基本操作

一、数据库的基本概念 1、数据库的组成 数据&#xff1a;描述事物的符号记录 包括数字&#xff0c;文字、图形、图像、声音、档案记录等 以“记录”形式按统一格式进行存储 表&#xff1a;将不同的记录组织在一起&#xff0c;用来存储具体数据 数据库&#xff1a; 表的集合…

【开源】基于Vue和SpringBoot的服装店库存管理系统

项目编号&#xff1a; S 052 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S052&#xff0c;文末获取源码。} 项目编号&#xff1a;S052&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 服…

AT89S52单片机

目录 一.AT89S52单片机的硬件组成 1.CPU(微处理器) (1)运算器 (2)控制器 2.数据存储器 (RAM) (1)片内数据存储器 (2)片外数据存储器 3.程序存储器(Flash ROM) 4.定时器/计数器 5.中断系统 6.串行口 7.P0口、P1口、P2口和P3口 8.特殊功能寄存器 (SFR) 常用的特殊功…

>Web 3.0顶级干货教学:浅析区块链与货币关系

Web 3.0顶级干货教学&#x1f525;&#xff1a;浅析区块链与货币关系 尊重原创&#xff0c;编写不易 &#xff0c;帮忙点赞关注一下~转载小伙伴请注明出处&#xff01;谢谢 1.0 数字交易 最早一笔数字化交易 是在www.PizzaHut.com 在 1994 年产生的&#xff0c;但是有趣的事情…

79基于matlab的大米粒中杂质识别

基于matlab的大米粒中杂质识别&#xff0c;数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。 79matlab图像处理杂质识别 (xiaohongshu.com)

JAVA sql 查询2

SELECT * FROM employees order by salayr DESC SELECT employee_id,first_name,salary from employees ORDER BY salary,employee_id desc -- 最大值 最小值 总和 平均值 SELECT max(salary),MIN(salary),sum(salary),AVG(salary) FROM employees -- 总共有多少员工 select…

vb加了me

Imports System.Data.OleDbPublic Class Form1小目录简写Dim jiuyue As String() {"创", "出", "利", "民", "申", "书", "士", "得", "撒上", "撒下", "王上&quo…

洛谷 P3252 [JLOI2012] 树

读题就读趋势了&#xff0c;还以为是每个深度都可以选一个&#xff0c;然后深度升序就可以了&#xff0c;以为是个按深度的01背包。 但是前面还说了是一条路径&#xff0c;路径是不能断开的。那就从每个点开始爆搜一次就好了。 看了一下范围n<1e5&#xff0c;n^2爆搜理论上…

css 实现鼠标上移添加下划线

效果图 实现代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wi…

Using Set Processing Examples 使用集合处理示例

Using Set Processing Examples 使用集合处理示例 Each of the following topics contains an example of set processing. 以下每个主题都包含一个集处理示例。 Payroll 工资单 In this example, suppose the payroll department needs to give a 1000 USD salary increase to…

开发上门洗衣洗鞋小程序都需要考虑哪些经营场景

互联网的高速发展让很多行业都转变了传统的服务模式&#xff0c;很多需要到店的服务都提供了上门的服务方式&#xff0c;洗护行业也是如此&#xff0c;越来越多的城市都开始流行上门洗衣洗鞋&#xff0c;要做上门的服务模式的话&#xff0c;就需要有一个小程序为载体&#xff0…

离散数学考前小记

数理逻辑 求前束范式的一般步骤&#xff1a; 利用等值公式消去“ → \rightarrow →”和“ ↔ \leftrightarrow ↔”否定深入改名前移量词 仅含有全称量词的前束范式称为SKOLEM标准形。 SKOLEM标准形的求解算法&#xff1a; 先求谓词演算公式的前束范式使用n元函数干掉存在…

Mysql Shell笔记

Mysql Shell部署 cd /usr/local/ tar -xvf /root/mysql-shell-8.0.35-linux-glibc2.17-x86-64bit.tar.gz chown -R mysql.mysql mysqlsh mysql-shell-8.0.35-linux-glibc2.17-x86-64bitmysqlsh登录退出 mysqlsh -uroot -S /data/3306/mysql.sock MySQL Shell 8.0.35 Copyrigh…

【鸿蒙应用ArkTS开发系列】- 灌水区,鸿蒙ArkTs开发有问题可以在该帖中反馈

大家好, 这是一篇水贴&#xff0c;给大家提供一个交流沟通鸿蒙开发遇到问题的地方。 新增新增这个文章呢&#xff0c;大家在开发使用ArkTS开发鸿蒙应用或者鸿蒙服务的时候&#xff0c;有遇到疑问或者问题&#xff0c;可以在本文章评论区提问&#xff0c;我看到了如果知道怎么…

PCL 计算一条射线与一个球的相交点

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 与直线相同,射线往往也可以用点向式来进行表示: O + t D O+tD O+

知识库文档处理

知识库文档处理 1 知识库设计2 文档加载2.1 PDF文档2.2 MD文档2.3 MP4视频 3 文档分割4 文档词向量化 本项目是一个个人知识库助手项目&#xff0c;旨在帮助用户根据个人知识库内容&#xff0c;回答用户问题。个人知识库应当能够支持各种类型的数据&#xff0c;支持用户便捷地导…

List操作的一些常见问题

文章目录 阿里巴巴开发手册强制规约&#xff1a;1. Arrays.asList转换基本类型数组2. Arrays.asList返回的List不支持增删操作3. 对原始数组的修改会影响到我们获得的那个List4. ArrayList.subList强转ArrayList导致异常5. ArrayList中的subList切片造成OOM6.Copy-On-Write 是什…