【数据结构】栈和队列的模拟实现(两个方式实现)
前言
💓作者简介: 加油,旭杏,目前大二,正在学习C++,数据结构等👀
💓作者主页:加油,旭杏的主页👀⏩本文收录在:再识C进阶的专栏👀
🚚代码仓库:旭日东升 1👀
🌹欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖
学习目标:
这一篇博客将学习栈和队列的相关知识,栈和队列是两种基础的数据结构,在现在一定要打好基础,在之后的学习生涯中,也常常遇见,例如:深度优先搜索(DFS)广度优先搜索(BFS)……今天要学习栈和队列的模拟实现:用数组模拟实现栈,用单链表模拟实现队列,用数组模拟实现队列。
学习内容:
通过上面的学习目标,我们可以列出要学习的内容:
- 用数组模拟实现栈
- 用数组模拟实现队列
- 用单链表模拟实现队列
一、栈的相关知识
1.1 栈的概念及结构
下面先来一段大白话,在介绍栈的文章中都会出现的。
栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作(类似于尾插尾删)。我们先来介绍两个概念——栈顶与栈底:进行数据插入和删除操作的一端为栈顶,而另一端为栈底。高大上一点的叫法是:栈中的元素遵守后进先出LIFO(Last In First Out)的原则。
下面,我们在来介绍两个操作——圧栈与入栈:
- 压栈:栈的插入操作是进栈/压栈/入栈,入数据在栈顶;
- 出栈:栈的删除操作是出栈,出数据在栈顶。
1.2 栈的实现
现在,我们学习了两个数据结构:顺序表与链表,到底用哪个数据结构实现栈比较好呢?在这里我们选用数组来模拟实现栈。
为什么我们要用数组模拟实现栈会更好一些?
栈的两个操作无疑就是尾插与尾删,这两个操作在数组中都比较容易实现(相对于链表),而且数组存储的数据内容比较集中,CPU高速缓存命中率高,尽可能小地不污染缓存区,所以用数组模拟实现比较好一些。
在现实生活中,我们一般要实现能够进行动态增长的栈,而不是静态的栈(比较死板,在实际应用中不常见)。虽然,静态的栈在实际生活中不常见,但是在算法题目中还是可以进行使用,而且在算法题中,我们不用考虑内存泄漏问题hh。下面来实现吧!
代码一:实现动态的栈
第一步,我们先来定义要使用的头文件:
#include <stdio.h> //必须要包含的头文件 #include <assert.h> //要使用assert进行断言,防止出现一下不好的情况发生 #include <stdbool.h> //在C语言中,没有提供bool变量 #include <stdlib.h> //使用一些申请内存空间的函数
第二步,我们要让主角登场,构造栈的结构体:
typedef int StDatatype; // 为了方便以后更改数组中的数据类型 typedef struct stack {StDatatype* a; // 一个动态空间int top; // 表示栈的栈顶int capacity; // 表示栈现在有多少空间 }ST;
第三步,我们要定义一些我们要实现栈功能的一些函数:栈的初始化(定义一个结构,一定要初始化成员变量)、栈的销毁、栈的插入操作、栈的删除操作、查看栈的栈顶元素、查看栈的大小、查看栈是否为空
//栈的初始化 void stackinit(ST* stk); //栈的插入操作 void stackpush(ST* stk, StDatatype x); //栈的删除操作 void stackpop(ST* stk); //返回栈的栈顶元素 StDatatype stacktop(ST* stk); //判断栈是否为空 bool stackempty(ST* stk); //返回栈的大小 int stacksize(ST* stk); //销毁栈 void stackdestroy(ST* stk);
栈的初始化操作:
void stackinit(ST* stk) {assert(stk); //防止传空指针//有两个方式进行初始化//法1stk->a = NULL;stk->capacity = 0;stk->top = 0; //如果这里指向0,top表示的是栈的大小 }//法2 void stackinit(ST* stk) {assert(stk);stk->a = NULL;stk->capacity = 0;stk->top = -1; //如果这里指向-1,top表示的是栈顶元素的下标 }
栈的销毁操作:
void stackdestroy(ST* stk) {assert(stk);free(stk->a);stk->a = NULL; // 别忘记置空,防止野指针出现free(stk);stk = NULL; } // 因为栈所使用的是数组,所以直接销毁即可
栈的插入操作:
void stackpush(ST* stk, StDatatype x) {assert(stk);//因为我们在这里的栈的空间是没有创建的if (stk->capacity == stk->top) {int newcapacity = stk->capacity == 0 ? 4 : stk->capacity * 2;StDatatype* tmp = (StDatatype*)realloc(stk->a, newcapacity * sizeof(StDatatype));if (tmp == NULL) {perror(tmp);return;}stk->a = tmp;stk->capacity = newcapacity;} //插入操作相当于尾插stk->a[stk->top] = x;stk->top++; // 别忘了栈顶要加1 }
栈的删除操作:
void stackpop(ST* stk) {assert(stk);assert(stk->top > 0);stk->top--; }
返回栈的栈顶元素:
StDatatype stacktop(ST* stk) {assert(stk);assert(stk->top > 0);return stk->a[stk->top - 1]; // top == 0//return stk->a[stk->top]; // top == -1 }
判断栈是否为空:
bool stackempty(ST* stk) { // 第一种写法:assert(stk);if (stk->top == 0){return true;}return false; // 第二种写法://return stk->top == 0; }
返回栈的大小:
int stacksize(ST* stk) {assert(stk);return stk->top; // top == 0//return stk->top + 1; // top == -1 }
代码二:实现静态的栈
struct my_stack
{int a[10];int size;int capacity;
}
二、队列的相关知识
2.1 队列的概念及结构
队列在日常生活中无处不在,排队就是一种典型的队列。对此,我们大致可以得知:队列是只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的性质。和栈一样,队列具有两个基本操作——入队和出队:
入队:进行插入操作的一端称为队尾;
出队:进行删除操作的一端称为队头。
2.2 队列的实现
与实现栈类似,我们先来讨论一下使用哪种数据结构方便一些?是使用顺序表呢,还是使用链表呢?是使用单链表呢还是使用双向链表?
分析一下队列的插入和删除操作,发现插入操作是尾插,而删除操作是头删。在实现顺序表和链表时,对于头删操作来说,链表更为简单,而顺序表需要进行移动数据,效率较低。因此,我们使用链表来实现操作。
代码一:链表实现队列
第一步,我们还是先将头文件加上:
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <assert.h>
第二步,我们来建立一下队列的数据结构:
typedef int QueDatatype;typedef struct queue {QueDatatype x;struct queue* next; }Queue;//由于要用二级指针,并且我们还有多个成员, //然后就用结构体typedef struct Qnode {Queue* phead;Queue* ptail; // 存储链表的尾结点,方便于尾插int size; }Qnode;
第三步,我们来定义一下队列的功能:队列的初始化,队列的销毁,队列的插入操作,队列的删除操作,返回队列的队头元素,返回队列的大小,检查队列是否为空。
//队列初始化 void queueinit(Qnode* qnd); //队列销毁 void queuedestroy(Qnode* qnd); //队列的插入操作 void queuepush(Qnode* qnd, QueDatatype x); //队列的删除操作 void queuepop(Qnode* qnd); //队列的对头元素 QueDatatype queuefront(Qnode* qnd); //队列的大小 int queuesize(Qnode* qnd); //检查队列是否为空 bool quueueempty(Qnode* qnd);
队列的初始化操作:
void queueinit(Qnode* qnd) {assert(qnd);qnd->phead = qnd->ptail = NULL;qnd->size = 0; }
队列的销毁操作:
void queuedestroy(Qnode* qnd) {assert(qnd);//销毁是要从头到尾进行销毁的Queue* cur = qnd->phead;while (cur){Queue* del = cur->next;free(cur);cur = del;} // 只有一个结点的情况,会出现一个野指针qnd->phead = qnd->ptail = NULL;qnd->size = 0; }
队列的插入操作:
void queuepush(Qnode* qnd, QueDatatype x) {assert(qnd);//创建好了结构体,然后进行操作Queue* tmp = (Queue*)malloc(sizeof(Queue));//防止创建失败if (tmp == NULL){perror(tmp);return;}tmp->x = x;tmp->next = NULL;//将这个新结点链接到链表中if (qnd->phead == NULL){qnd->phead = qnd->ptail = tmp;}else {qnd->ptail->next = tmp;qnd->ptail = tmp;}qnd->size++; }
队列的删除操作:
void queuepop(Qnode* qnd) {assert(qnd);//头结点不能为空assert(qnd->phead);//其次,在只有一个结点的过程中,会出现野指针Queue* del = qnd->phead;qnd->phead = del->next;free(del);del = NULL;if (qnd->phead == NULL){qnd->ptail = NULL;}qnd->size--; }
返回队列的队头元素:
QueDatatype queuefront(Qnode* qnd) {assert(qnd);//不能队列为空assert(qnd->phead);return qnd->phead->x; }
返回队列的大小:
int queuesize(Qnode* qnd) {assert(qnd);return qnd->size; }
检查队列是否为空:
bool quueueempty(Qnode* qnd) {assert(qnd);//if (qnd->phead ==NULL)//{// return true;//}//return false;return qnd->phead == NULL; }
代码二:数组实现队列
int q[N], hh, tt = -1;
学习产出:
- 用数组模拟实现栈
- 用数组模拟实现队列
- 用单链表模拟实现队列
相关文章:
【数据结构】栈和队列的模拟实现(两个方式实现)
前言 💓作者简介: 加油,旭杏,目前大二,正在学习C,数据结构等👀 💓作者主页:加油,旭杏的主页👀 ⏩本文收录在:再识C进阶的专栏…...
OpenCV+相机校准和3D重建
相机校准至少需要10个测试图案,所需的重要输入数据是3D现实世界点集以及图像中这些点的相应2D坐标。3D点称为对象点,而2D图像点称为图像点。 准备工作 除了棋盘,我们还可以使用圆形网格。 在这种情况下,我们必须使用函数cv.find…...
2023.11.14-hive之表操作练习和文件导入练习
目录 需求1.数据库基本操作 需求2. 默认分隔符案例 需求1.数据库基本操作 -- 1.创建数据库test_sql,cs1,cs2,cs3 create database test_sql; create database cs1; create database cs2; create database cs3; -- 2.1删除数据库cs2 drop database cs2; -- 2.2在cs3库中创建…...
idea2023启动springboot项目如何指定配置文件
方法一: 方法二: 举例:...
在 uniapp 中 一键转换单位 (px 转 rpx)
在 uniapp 中 一键转换单位 px 转 rpx Uni-app 官方转换位置利用【px2rpx】插件Ctrl S一键全部转换下载插件修改插件 Uni-app 官方转换位置 首先在App.vue中输入这个: uni.getSystemInfo({success(res) {console.log("屏幕宽度", res.screenWidth) //屏…...
SQL对数据进行去重
工作中使用SQL对数据进行处理计算时可能会遇到这样的问题;读取的表数据会有重复,或者我们关注的几个字段的数据会有重复,直接使用原表数据会引起计算结果不准或者做表连接时产生笛卡尔积。 本文记录使用SQL进行数据去重的几种算法。 distinc…...
登录注册代码模板(Vue3+SpringBoot)[邮箱发送验证码(HTML)、RSA 加密解密(支持长文本)、黑暗与亮色主题切换、AOP信息校验]
文章归档:https://www.yuque.com/u27599042/coding_star/cx5ptule64utcr9e 仓库地址 https://gitee.com/tongchaowei/login-register-template 网页效果展示 相关说明 在该代码模板中,实现了如下功能: 邮箱发送验证码(邮件内容…...
【计算机网络】VRRP协议理论和配置
目录 1、VRRP虚拟路由器冗余协议 1.1、协议作用 1.2、名词解释 1.3、简介 1.4、工作原理 1.5、应用实例 2、 VRRP配置 2.1、配置命令 2.2、拓扑与配置: 1、VRRP虚拟路由器冗余协议 1.1、协议作用 虚拟路由冗余协议(Virtual Router Redundancy Protocol&am…...
ubuntu操作系统的docker更换存储目录
前言 要将Docker的存储目录更改为/home/docker,你需要进行以下步骤: 目录 前言1、停止Docker服务2、创建新的存储目录3、编辑Docker配置文件4、启动Docker服务5、验证更改 1、停止Docker服务 首先停止Docker守护进程,可以使用以下命令&…...
【人工智能Ⅰ】6-机器学习之分类
【人工智能Ⅰ】6-机器学习之分类 6-1 机器学习在人工智能中的地位 学习能力是智能的本质 人工智能 > 机器学习 > 深度学习 什么是机器学习? baidu:多领域交叉学科(做什么) wiki:the study of algorithms and…...
本地部署_语音识别工具_Whisper
1 简介 Whisper 是 OpenAI 的语音识别系统(几乎是最先进),它是免费的开源模型,可供本地部署。 2 docker https://hub.docker.com/r/onerahmet/openai-whisper-asr-webservice 3 github https://github.com/ahmetoner/whisper…...
秋招求职经验分享
0.个人简介 2023年10月底,最终拿到了海康威视、汇川技术等十余家公司的Offer,最终签了自己心仪的Offer,秋招对我来说算是正式结束了,写个博客纪念一下,顺便分享以下秋招的经验,为后来人求职提供一些参考。…...
DNS域名解析
目录 1.概述 1.1产生原因 1.2作用 1.3连接方式 1.4因特网的域名结构 1.4.1拓扑 1.4.2分类 1.4.3域名服务器类型划分 2. DNS域名解析过程 2.1分类 2.2解析图 2.2.2过程分析 3.搭建DNS域名解析服务器 3.1.概述 3.2安装软件 3.3bind服务中三个关键文件 3.4主配置…...
Flink SQL --命令行的使用(02)
1、窗口函数: 1、创建表: -- 创建kafka 表 CREATE TABLE bid (bidtime TIMESTAMP(3),price DECIMAL(10, 2) ,item STRING,WATERMARK FOR bidtime AS bidtime ) WITH (connector kafka,topic bid, -- 数据的topicproperties.bootstrap.servers m…...
【nlp】1.3 文本数据分析(标签数量分布、句子长度分布、词频统计与关键词词云)
文本数据分析 1 文本数据分析介绍2 数据集说明3 获取标签数量分布4 获取句子长度分布5 获取正负样本长度散点分布6 获取不同词汇总数统计7 获取训练集高频形容词词云8 获取验证集形容词词云1 文本数据分析介绍 文本数据分析的作用: 文本数据分析能够有效帮助我们理解数据语料…...
路由器的结构以及工作原理
目录 路由器的结构 交换结构三种常用的交换方式 1.通过存储器 2.通过总线 3.通过纵横交换结构(crossbar switch fabric) 路由器的结构 路由器结构可划分为两大部分:路由选择部分,分组转发部分 路由选择部分也叫做控制部分&…...
DefaultListableBeanFactory
DefaultListableBeanFactory 是一个完整的、功能成熟的 IoC 容器,如果你的需求很简单,甚至可以直接使用 DefaultListableBeanFactory,如果你的需求比较复杂,那么通过扩展 DefaultListableBeanFactory 的功能也可以达到,…...
NSF服务器
目录 1.简介 1.1 NFS背景介绍 1.2 生产应用场景 2.NFS工作原理 2.1 实例图 2.2 流程 3.NFS的使用 3.1.安装 3.2.配置文件 3.3.主配置文件分析 3.4 实验 服务端: 客户端: 3.5.NFS账户映射 3.5.1.实验2 3.5.2.实验3 4.autofs自动挂载服务…...
10 Go的映射
概述 在上一节的内容中,我们介绍了Go的结构体,包括:定义结构体、声明结构体变量、使用结构体、结构体关联函数、new、组合等。在本节中,我们将介绍Go的映射。Go语言中的映射(Map)是一种无序的键值对集合&am…...
瑞萨e2studio(29)----SPI速率解析
瑞萨e2studio.29--SPI速率解析 概述视频教学时钟配置解析RA4M2的BRR值时钟速率7.5M下寄存器值3K下寄存器值 概述 在嵌入式系统的设计中,串行外设接口(SPI)的通信速率是一个关键参数,它直接影响到系统的性能和稳定性。瑞萨电子的…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
