【数据结构】数组实现队列(详细版)

目录

队列的定义

普通顺序队列的劣势——与链队列相比 

顺序队列实现方法:

一、动态增长队列

 1、初始化队列

 2、元素入队

 3、判断队列是否为空

 4、元素出队

 5、获取队首元素

6、获取队尾元素

 7、获取队列元素个数

8、销毁队列

 总结:

动态增长队列完整测试代码:

二、固定长度队列 

1、与动态增长队列的差异

2、判断是否队满 

固定长度队列完整测试代码:


本节我们采用数组(顺序表)形式实现队列,学习单链表实现请点击下方链接:

队列—单链表实现(C语言版)-CSDN博客

为了减少数组初始长度过大对内存空间的浪费,本节我们采用动态内存管理,相关函数请的介绍点击下方链接:

动态内存函数(malloc,free,calloc,realloc)-CSDN博客

循环队列的实现:

循环队列(数组实现)-CSDN博客



 

队列的定义

队列是一种基本的数据结构,它是一种先进先出(First In First Out,FIFO)的线性结构队列只允许在表的一端进行插入,而在另一端进行删除操作。这就相当于把数据排成一排,先插入的排在前面,后插入的排在后面,之后进行删除操作时也只能从前面依次删除。这种数据结构一般用于需要按照先后顺序进行处理的问题,如模拟系统、计算机网络中的缓存、操作系统中的进程调度等。队列的基本操作包括入队(插入元素到队尾)出队(从队头删除元素),队列还有一个重要的特性就是队列的长度是动态变化的,随着入队和出队的操作进行不断变化。

 ​​

普通顺序队列的劣势——与链队列相比 

  1. 长度固定:普通数组队列的长度是固定的,一旦数组被分配,其长度无法改变。当队列元素数量超过数组长度时,需要进行数组的扩容操作,这会导致性能上的开销。

  2. 内存的浪费:因为普通数组队列的长度固定,可能会出现队列中存在空闲的位置,导致内存的浪费。

为了解决上述 问题1,我们在本节中对顺序表采取动态内存管理,在必要时更新数组的长度,以保证顺序队列的长度足够使用。

 (补充:问题2 的解决需要使用循环队列,本节内容先为大家介绍一般队列的实现,等同学们对队列有了充分的理解之后,我们下节再进行循环队列的学习。)


顺序队列实现方法:

一、我们首先定义一个数组,数组的头部为队首,尾部为队尾。每当插入一个元素时,就将元素放在队尾,当删除一个元素时,将队首的元素删除。当队列为空时,不能再删除元素。

二、我们采用双指针法时刻记录队列的队首和队尾:

  1. 定义一个固定大小的数组作为队列的存储空间,并定义两个指针front和rear分别指向队列的队首和队尾。

  2. 初始化队列时,将front和rear都设置为0,表示队列为空。

  3. 插入元素时,将元素放入rear指针指向的位置,并将rear指针后移一位。

  4. 删除元素时,将front指针后移一位。

  5. 判断队列是否为空,只需要判断front和rear是否相等即可。


一、动态增长队列

 1、初始化队列

初始化队列时,将front和rear都设置为0,表示队列为空。

typedef int DataType;typedef struct Queue
{DataType* a; // 队列的数组int front, rear; // 队列的头部和尾部位置索引int size; // 队列中元素的数量int capacity; // 队列的容量
} Queue;// 初始化队列
void InitQueue(Queue* q)
{q->a = NULL; // 数组指针初始化为NULLq->front = q->rear = 0; // 头部和尾部位置索引初始化为0q->size = q->capacity = 0; // 元素数量和容量都初始化为0
}

 2、元素入队

// 入队
void QueuePush(Queue* q, DataType x)
{assert(q); // 断言q不为NULLif (q->capacity == q->rear){// 如果队列已满,进行扩容操作int new_capacity = q->capacity == 0 ? 10 : q->capacity * 2; // 扩容的大小为原容量的2倍DataType* temp = (DataType*)realloc(q->a, new_capacity * sizeof(DataType)); // 重新分配内存空间if (temp == NULL){perror("realloc fail"); // 扩容失败,则输出错误信息exit(-1); // 退出程序}q->capacity = new_capacity; // 更新队列的容量q->a = temp; // 更新数组指针}q->a[q->rear++] = x; // 在尾部插入新元素,并更新尾部位置索引q->size++; // 元素数量加1
}

 3、判断队列是否为空

判断队列是否为空,只需要判断front和rear是否相等即可。

// 判断队列是否为空
bool QueueEmpty(Queue* q)
{assert(q); // 断言q不为NULLif (q->front == q->rear){return true; // 头部和尾部位置索引相等,队列为空}return false; // 队列不为空
}

 4、元素出队

 删除元素时,将front指针后移一位。

void QueuePop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){q->front++; // 更新头部位置索引q->size--; // 元素数量减1}else{printf("队列已空,删除失败!\n"); // 队列为空,无法出队}
}

5、获取队首元素

// 获取队首元素
DataType QueueTop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->front]; // 返回队首元素的值}else{printf("队列已空,获取队头元素失败!\n"); // 队列为空,无法获取队首元素exit(-1); // 退出程序}
}

6、获取队尾元素

队尾指针q->rear在最后一个元素的下一位,所以我们返回队尾元素时需要返回队尾坐标的前一个坐标所指向的元素。

// 获取队尾元素
DataType QueueTail(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->rear - 1]; // 返回队尾元素的值}else{printf("队列已空,获取队尾元素失败!\n"); // 队列为空,无法获取队尾元素exit(-1); // 退出程序}
}

7、获取队列元素个数

// 获取队列中元素的数量
int QueueSize(Queue* q)
{assert(q); // 断言q不为NULLreturn q->size; // 返回元素数量
}

8、销毁队列

// 销毁队列
void QueueDestory(Queue* q)
{assert(q); // 断言q不为NULLfree(q->a); // 释放队列的数组空间q->a = NULL; // 数组指针置为NULL
}

总结:

 通过对顺序队列的学习我们可以明显看到顺序队列的缺点。当我们删除队首元素后由于队列只能从队尾进行增加元素的操作,所以front指针之前的空间不能再进行使用

如果是在队列长度是固定长度的情况下,当队尾指针rear到达最大时,队列已满,数组内已经没有空间进行插入操作,但由于此时front指针前可能还有空余空间,这时我们就造成了空间的浪费。

我们把这种现象称为“假溢出”现象。那么通过数组的循环队列或者链队列我们可以很好的解决此类现象。

下节我们将对如何用数组实现循环队列进行介绍:循环队列(数组实现)-CSDN博客

动态增长队列完整测试代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int DataType;typedef struct Queue
{DataType* a; // 队列的数组int front, rear; // 队列的头部和尾部位置索引int size; // 队列中元素的数量int capacity; // 队列的容量
} Queue;// 初始化队列
void InitQueue(Queue* q)
{q->a = NULL; // 数组指针初始化为NULLq->front = q->rear = 0; // 头部和尾部位置索引初始化为0q->size = q->capacity = 0; // 元素数量和容量都初始化为0
}// 判断队列是否为空
bool QueueEmpty(Queue* q)
{assert(q); // 断言q不为NULLif (q->front == q->rear){return true; // 头部和尾部位置索引相等,队列为空}return false; // 队列不为空
}// 入队
void QueuePush(Queue* q, DataType x)
{assert(q); // 断言q不为NULLif (q->capacity == q->rear){// 如果队列已满,进行扩容操作int new_capacity = q->capacity == 0 ? 10 : q->capacity * 2; // 扩容的大小为原容量的2倍DataType* temp = (DataType*)realloc(q->a, new_capacity * sizeof(DataType)); // 重新分配内存空间if (temp == NULL){perror("realloc fail"); // 扩容失败,则输出错误信息exit(-1); // 退出程序}q->capacity = new_capacity; // 更新队列的容量q->a = temp; // 更新数组指针}q->a[q->rear++] = x; // 在尾部插入新元素,并更新尾部位置索引q->size++; // 元素数量加1
}// 出队
void QueuePop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){q->front++; // 更新头部位置索引q->size--; // 元素数量减1}else{printf("队列已空,删除失败!\n"); // 队列为空,无法出队}
}// 获取队首元素
DataType QueueTop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->front]; // 返回队首元素的值}else{printf("队列已空,获取队头元素失败!\n"); // 队列为空,无法获取队首元素exit(-1); // 退出程序}
}// 获取队尾元素
DataType QueueTail(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->rear - 1]; // 返回队尾元素的值}else{printf("队列已空,获取队尾元素失败!\n"); // 队列为空,无法获取队尾元素exit(-1); // 退出程序}
}// 获取队列中元素的数量
int QueueSize(Queue* q)
{assert(q); // 断言q不为NULLreturn q->size; // 返回元素数量
}// 销毁队列
void QueueDestory(Queue* q)
{assert(q); // 断言q不为NULLfree(q->a); // 释放队列的数组空间q->a = NULL; // 数组指针置为NULL
}int main()
{Queue q;InitQueue(&q);QueuePush(&q, 5);QueuePush(&q, 6);QueuePush(&q, 7);QueuePush(&q, 8);QueuePush(&q, 9);QueuePush(&q, 10);DataType x;x = QueueTop(&q);printf("%d\n", x);x = QueueTail(&q);printf("%d\n", x);QueuePop(&q);QueuePop(&q);QueuePop(&q);QueuePop(&q);QueuePop(&q);x = QueueTop(&q);printf("%d\n", x);x = QueueSize(&q);printf("%d\n", x);QueueDestory(&q);return 0;
}

二、固定长度队列 

1、与动态增长队列的差异

由于固定长度队列无需扩容,所以不需要进行动态内存的分配,也不需要进行销毁队列的操作

同时相对于动态增长的队列,固定长度的队列需要判断队内元素数量是否达到了队列的最大容量。由于我们在代码中是先对队尾指针rear指向的位置添加元素,再对rear进行自增,更新队尾索引,所以在本代码中队满的判断条件是rear==MAXLEN


2、判断是否队满 

当对固定长度队列添加元素时,如果当前队列队尾指针已达到数组长度,由于队列只能从队尾添加元素,此时我们不能再为队列添加新的元素。所以在我们为队尾添加元素时,我们首先要判断队列是否已满——即队尾指针是否达到数组容量的最大值。

//判断队列是否为满
bool QueueFull(Queue* q)
{assert(q); // 断言q不为NULLif (q->rear == MAXLEN){return true; // 尾部位置达到数组长度最大值,队列为满}return false; // 队列不为满
}

明白了以上几点,我们对动态增长队列的代码稍作修改,添加判断队列是否已满的函数并对增加队列元素作出限制,就可得到固定长度队列的代码。

固定长度队列完整测试代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>#define MAXLEN 10
typedef int DataType;typedef struct Queue
{DataType a[MAXLEN]; // 队列的数组int front, rear; // 队列的头部和尾部位置索引int size; // 队列中元素的数量
} Queue;// 初始化队列
void InitQueue(Queue* q)
{assert(q);q->front = q->rear = 0; // 头部和尾部位置索引初始化为0q->size = 0; // 元素数量初始化为0
}// 判断队列是否为空
bool QueueEmpty(Queue* q)
{assert(q); // 断言q不为NULLif (q->front == q->rear){return true; // 头部和尾部位置索引相等,队列为空}return false; // 队列不为空
}//判断队列是否为满
bool QueueFull(Queue* q)
{assert(q); // 断言q不为NULLif (q->rear == MAXLEN){return true; // 尾部位置达到数组长度最大值,队列为满}return false; // 队列不为满
}// 入队
void QueuePush(Queue* q, DataType x)
{assert(q); // 断言q不为NULLif (!QueueFull(q))//判断队列是否为满{q->a[q->rear++] = x; // 在尾部插入新元素,并更新尾部位置索引q->size++; // 元素数量加1}else{printf("队列已满\n");exit(-1);}
}// 出队
void QueuePop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){q->front++; // 更新头部位置索引q->size--; // 元素数量减1}else{printf("队列已空,删除失败!\n"); // 队列为空,无法出队}
}// 获取队首元素
DataType QueueTop(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->front]; // 返回队首元素的值}else{printf("队列已空,获取队头元素失败!\n"); // 队列为空,无法获取队首元素exit(-1); // 退出程序}
}// 获取队尾元素
DataType QueueTail(Queue* q)
{assert(q); // 断言q不为NULLif (!QueueEmpty(q)){return q->a[q->rear - 1]; // 返回队尾元素的值}else{printf("队列已空,获取队尾元素失败!\n"); // 队列为空,无法获取队尾元素exit(-1); // 退出程序}
}// 获取队列中元素的数量
int QueueSize(Queue* q)
{assert(q); // 断言q不为NULLreturn q->size; // 返回元素数量
}int main()
{Queue q;InitQueue(&q);QueuePush(&q, 5);QueuePush(&q, 6);QueuePush(&q, 7);QueuePush(&q, 8);QueuePush(&q, 9);QueuePush(&q, 10);QueuePush(&q, 5);QueuePush(&q, 6);QueuePush(&q, 7);QueuePush(&q, 8);//QueuePush(&q, 9);//QueuePush(&q, 10);DataType x;x = QueueTop(&q);printf("%d\n", x);x = QueueTail(&q);printf("%d\n", x);QueuePop(&q);QueuePop(&q);QueuePop(&q);QueuePop(&q);QueuePop(&q);x = QueueTop(&q);printf("%d\n", x);x = QueueSize(&q);printf("%d\n", x);return 0;
}

如果有同学在部分地方有疑惑,欢迎评论区讨论。

本节内容告一段落,我们下节博客见。

循环队列(数组实现)-CSDN博客

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

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

相关文章

whistle+SwitchyOmega前端api代理调试

1、whistle介绍 whistle官网whistle githubwhistle主要用于查看、修改HTTP、HTTPS、Websocket的请求、响应&#xff0c;也可以作为HTTP代理服务器&#xff0c;功能很强大 2、安装教程 官方安装文档 // 全局安装whistle npm install -g whistle// 安装whistle的inspect插件&a…

2024.1.3 关于 Redis 渐进式遍历 和 数据库管理命令

目录 引言 渐进式遍历 SCAN 命令 数据库管理命令 切换数据库 获取数据库 key 个数 删除数据库所有 key 同步删除 SYNC 异步删除 ASYNC 阅读下述文章之前建议点击下方链接熟悉 keys 命令的用法和特点 Redis 全局通用命令 ​​​渐进式遍历 keys * 命令一次性将 Redi…

安全狗入选“2023年福建省信息技术应用创新解决方案”名单

近日&#xff0c;福建省数字福建建设领导小组办公室公布了2023年福建省信息技术应用创新解决方案入选项目评选结果。 作为国内云原生安全领导厂商&#xff0c;安全狗凭借综合且具备突出创新水平的方案入选。 厦门服云信息科技有限公司&#xff08;品牌名&#xff1a;安全狗&…

栈和队列oj题——232. 用栈实现队列

. 个人主页&#xff1a;晓风飞 专栏&#xff1a;LeetCode刷题|数据结构|Linux|C语言 路漫漫其修远兮&#xff0c;吾将上下而求索 文章目录 题目要求&#xff1a;实现 MyStack 类&#xff1a;注意&#xff1a;示例&#xff1a;解释&#xff1a;提示&#xff1a; 解题核心概念数据…

LeetCode刷题---矩阵置零

解题思路&#xff1a; 本题要求原地置换元素 对矩阵进行第一轮遍历&#xff0c;使用第一行第一列来充当该行该列是否要置换为0的标记位&#xff0c;如果第一行或第一列本身就含有零元素&#xff0c;我们使用colZero和rowZero变量来对其标记。如果第i行第j列的那个元素为0&#…

how2heap-2.23-02-fastbin_dup_into_stack

fastbin_dup_into_stack和fastbin_dup没啥区别 https://blog.csdn.net/u014679440/article/details/135383465 仅仅是欲修改的位置&#xff0c;在栈中 #include <stdio.h> #include <stdlib.h>int main() {fprintf(stderr, "This file extends on fastbin_du…

leetcode13 罗马数字转整数

题目描述&#xff1a;罗马数字由七种字符组成&#xff0c;分别为 I、V、X、L、C、D 和 M&#xff0c;对应的数值分别为 1、5、10、50、100、500 和 1000。在一般情况下&#xff0c;小的数字位于大的数字右边&#xff0c;但有特殊情况&#xff0c;如 IV 表示 4&#xff0c;IX 表…

单线圈无刷直流电机驱动芯片选型分析,可应用于笔记本,显卡风散热风扇,变频冷却风扇,打印机风扇等产品上

单线圈无刷直流电机的电机驱动器。 GC1298R/S&#xff0c;GC1262E/S&#xff0c;GC1298R/S&#xff0c;GC1262R/S具有高效的直接PWM控制方式&#xff0c;它可以控制无刷直流电机转速。它集成了最低速度限制模式、可调速度斜率控制模式、软启动模式、风扇转速计、锁保护、自动重…

《剑指offer》数学第一题:数值的整数次方

题目描述&#xff1a; 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。 思路&#xff1a; 给定一个浮点数求它的整数次方。要考虑到所有的情况&#xff0c;关于指数&#xff0c;如果是0&#xff0c;则结果是1&#xff1b; 指数是1&#xff0c…

CNN——AlexNet

1.AlexNet概述 论文原文&#xff1a;ImageNet Classification with Deep Convolutional Neural Networks 在LeNet提出后&#xff0c;卷积神经网络在计算机视觉和机器学习领域中很有名气。但卷积神经网络并没有主导这些领域。这是因为虽然LeNet在小数据集上取得了很好的效果&am…

C#: Label、TextBox 鼠标停留时显示提示信息

说明&#xff1a;记录在 Label、TextBox 控件上 鼠标停留时显示提示信息的方法。 1.效果图 2.具体实现步骤 1. 在Form 窗口中先创建 Label 并取名&#xff1a;KEY_label &#xff0c;或 TextBox 取名&#xff1a;KEY_textBox 在 Form1 函数中添加初始化代码&#xff0c;如下&…

ssm基于web的素材网的设计与实现+vue论文

基于web的素材网站的设计与实现 摘要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。传统的素材信息管理模式&#xff0c;采用人工登记的方式保存相关数据&#xff0c;这种以人力为主的管理…

C#中使用 async await TaskCompletionSource<T>实现异步逻辑同步写

Task、async 和 await 是 C# 中用于处理异步编程的关键概念。它们一起构成了异步编程的基础。 Task Task 是表示异步操作的抽象&#xff0c;它属于 System.Threading.Tasks 命名空间。Task 可以表示已经完成的任务、正在运行的任务或者尚未开始的任务。通过 Task&#xff0c;…

你的第一个C/S程序

目录 socket服务端代码客户端代码执行结果 socket socket基础知识 服务端代码 import socket import threading import timeMSG_LENGTH 64 DISCONNECTED !CONNECTION CLOSED connections 0#定义服务器地址 server_ip socket.gethostbyname(socket.gethostname()) server…

【设计模式之美】面向对象分析方法论与实现(二):需求到接口实现的方法论

文章目录 一. 进行面向对象设计1. 划分职责>需要有哪些类2. 定义类及其属性和方法3. 定义类与类之间的交互关系4. 将类组装起来并提供执行入口 二. 如何进行面向对象编程&#xff1f;1. 接口实现2. 辩证思考与灵活应用 【设计模式之美】面向对象分析方法论与实现&#xff08…

教育场景数字化中音视频小程序的发展

教育场景数字化逐步成为刚需 2018年以来&#xff0c;国家对在线教育行业的监管收紧&#xff0c;以及受益于 5G 技术的发展&#xff0c;教育科技逐步走向成熟化和规范化。 教育行业的本质是人与人&#xff08;老师与学生、老师与家长&#xff0c;以及更多角色直接的沟通与互动…

【数据结构和算法】小行星碰撞

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 2.1 什么情况会用到栈 2.2 方法一&#xff1a;模拟 栈 三、代码 3.1 方法一&#xff1a;模拟 栈 四…

【LLM+RS】LLM在推荐系统的实践应用(华为诺亚)

note LLM用于推荐主要还是解决推荐系统加入open domain 的知识。可以基于具体推荐场景数据做SFT。学习华为诺亚-技术分享-LLM在推荐系统的实践应用。 文章目录 note一、背景和问题二、推荐系统中哪里使用LLM1. 特征工程2. 特征编码3. 打分排序 三、推荐系统中如何使用LLM四、挑…

共享WiFi贴项目加盟可以解决商家哪些痛点?

近年来&#xff0c;共享WiFi贴项目在共享商业领域引起了广泛关注。作为一种便捷的网络分享工具&#xff0c;共享WiFi贴不仅受到很多人的青睐&#xff0c;更能够为商家带来诸多实际利益。那么&#xff0c;共享WiFi贴项目加盟究竟可以解决商家哪些痛点呢&#xff1f; 共享WiFi贴为…

【C#】知识点实践序列之Lock的锁定代码块

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂之知识点实践序列》文章。 2024年第1篇文章&#xff0c;此篇文章是C#知识点实践序列之Lock知识点&#xff0c;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 本篇验证Lock锁定代…