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

news2025/11/7 10:42:29

目录

队列的定义

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

顺序队列实现方法:

一、动态增长队列

 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; // 数组指针初始化为NULL
    q->front = q->rear = 0; // 头部和尾部位置索引初始化为0
    q->size = q->capacity = 0; // 元素数量和容量都初始化为0
}

 2、元素入队

// 入队
void QueuePush(Queue* q, DataType x)
{
    assert(q); // 断言q不为NULL
    if (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不为NULL
    if (q->front == q->rear)
    {
        return true; // 头部和尾部位置索引相等,队列为空
    }
    return false; // 队列不为空
}

 4、元素出队

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

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

5、获取队首元素

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

6、获取队尾元素

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

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

7、获取队列元素个数

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

8、销毁队列

// 销毁队列
void QueueDestory(Queue* q)
{
    assert(q); // 断言q不为NULL
    free(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; // 数组指针初始化为NULL
    q->front = q->rear = 0; // 头部和尾部位置索引初始化为0
    q->size = q->capacity = 0; // 元素数量和容量都初始化为0
}

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

// 入队
void QueuePush(Queue* q, DataType x)
{
    assert(q); // 断言q不为NULL
    if (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不为NULL
    if (!QueueEmpty(q))
    {
        q->front++; // 更新头部位置索引
        q->size--; // 元素数量减1
    }
    else
    {
        printf("队列已空,删除失败!\n"); // 队列为空,无法出队
    }
}

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

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

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

// 销毁队列
void QueueDestory(Queue* q)
{
    assert(q); // 断言q不为NULL
    free(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不为NULL
    if (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; // 头部和尾部位置索引初始化为0
    q->size = 0; // 元素数量初始化为0
}

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

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

// 入队
void QueuePush(Queue* q, DataType x)
{
    assert(q); // 断言q不为NULL
    if (!QueueFull(q))//判断队列是否为满
    {
        q->a[q->rear++] = x; // 在尾部插入新元素,并更新尾部位置索引
        q->size++; // 元素数量加1
    }
    else
    {
        printf("队列已满\n");
        exit(-1);
    }
}

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

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

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

// 获取队列中元素的数量
int QueueSize(Queue* q)
{
    assert(q); // 断言q不为NULL
    return 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.coloradmin.cn/o/1355394.html

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

相关文章

企业数据存储监控

随着组织及其网络基础架构的不断扩展&#xff0c;存储将不可避免地成为一项挑战&#xff0c;随着存储需求的增长&#xff0c;调配更多存储资源的需求也会随之增长。为基础架构配置了更多存储资源后&#xff0c;它们需要不间断地运行&#xff0c;并且应该免受威胁。从本质上讲&a…

Linux之Ubuntu环境安装配置Jenkins

Ubuntu环境安装配置Jenkins&#xff0c;启动服务 一、安装过程 1、查看服务器的操作系统 lsb_release -a 2、查看JDK是否安装 java -version 如果还没有安装&#xff0c;则需要安装&#xff0c;命令如下&#xff1a; sudo apt install openjdk-11-jre-headless 3、下载2.…

pandas.DataFrame() 数据自动写入Excel

DataFrame 表格数据格式 &#xff1b; to_excel 写入Excel数据&#xff1b; read_excel 阅读 Excel数据函数 import pandas as pd#df2 pd.DataFrame({neme: [zhangsan, lisi, 3]}) df1 pd.DataFrame({One: [1, 2, 3],name: [zhangsan, lisi, 3]})#One是列明&#xff0c;123是…

前端三剑客——HTML5+CSS3+JavaScript

核心技术●实战训练营●项目实战&#xff08;微视频版&#xff09;   《前端三剑客——HTML5CSS3JavaScript》采用“核心技术→实战训练营→企业级项目实践”的结构和“由浅入深&#xff0c;由深到精”的模式进行讲解。 全书科学设置七大阶段由浅入深循序渐进&#xff0c;为解…

消防船,2027年将达到约26亿美元左右

近年来&#xff0c;消防船市场不断扩大&#xff0c;主要受到城市化进程和海上经济的快速发展的推动。消防船能够有效地灭火、增强海上边防力量&#xff0c;受到国内外政府和企业的广泛关注全球市场 近年来&#xff0c;消防船市场不断扩大&#xff0c;主要受到城市化进程和海上经…

C++ 多态向上转型详解

文章目录 1 . 前言2 . 多态3 . 向上转型4 . 总结 【极客技术传送门】 : https://blog.csdn.net/Engineer_LU/article/details/135149485 1 . 前言 此篇博文详解C的多态向上转型平台 : Qt 2 . 多态 【Q】什么是多态&#xff1f; 【A】解释如下 : 通俗来说,就是多种形态,具体…

Nginx学习之Nginx高性能的实现原理

Nginx学习之Nginx高性能的实现原理   Nginx 采用的是多进程&#xff08;单线程&#xff09; & 多路IO复用模型&#xff0c;使用了 I/O 多路复用技术的 Nginx&#xff0c;就成了”并发事件驱动“的服务器&#xff0c;同时使用sendfile等技术&#xff0c;最终实现了高性能。…

【LLM】大型语言模型综述论文

今天我将与大家分享一篇精彩的论文。这项调查提供了LLM文献的最新综述&#xff0c;这对研究人员和工程师来说都是一个有用的资源。 为什么选择LLM&#xff1f; 当参数尺度超过一定水平时&#xff0c;这些扩展的语言模型不仅实现了显著的性能改进&#xff0c;而且还表现出一些…

Ubuntu18.04 升级Ubuntu20.04

文章目录 背景升级方法遇到的问题 背景 因项目环境需要&#xff0c;欲将Ubuntu18.04升级至Ubuntu20.04&#xff0c;参考网上其他小伙伴的方法&#xff0c;也遇到了一个问题&#xff0c;特此记录一下&#xff0c;希望能帮助其他有同样问题的小伙伴。 升级方法 参考&#xff1a…

Qt+Opencv:基于Hough变换的直线检测

一、开场引言 在工业机器视觉领域&#xff0c;有很多直线检测和计算角度的应用场景。如下图&#xff0c;需要进行晶圆的粗对位校正&#xff1a; 此时需要计算出图像中的近似水平切割道的线条与实际水平线的夹角&#xff0c;从而进行晶圆的位置校正。在这个场景下&#xff0c;…

在Go语言中处理HTTPS请求

随着互联网的发展&#xff0c;安全性变得越来越重要。HTTPS作为安全的HTTP协议&#xff0c;已经被广泛使用。在Go语言中&#xff0c;处理HTTPS请求需要一些特定的步骤。本文将详细介绍如何在Go语言中处理HTTPS请求。 首先&#xff0c;确保你已经安装了Go语言的开发环境&#x…

如何删除K8S中的Pod

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

『C++成长记』运算符重载

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;C &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、运算符重载 &#x1f4d2;1.1两个日期大小的比较 &#x1f4d2;1.2运算符重载…

高通guestOS与hostOS通信框架HAB源码分析——概述

1)什么是HAB&#xff0c;他用来干什么&#xff1f; 如果你了解virtIO的话&#xff0c;就很容易明白HAB是用来干什么的。一句话来说&#xff0c;HAB实际作用和virtIO差不多。以高通8155&#xff08;host qnxguest安卓&#xff09;为例&#xff0c;所有硬件外设驱动都在qnx端&am…

公众号文章如何提高阅读量?媒介盒子教你几招

公众号作为微信运营的主要载体&#xff0c;做得好就能让品牌得到大量曝光&#xff0c;公众号文章作为长文案想要写好还需要一定的技术&#xff0c;今天媒介盒子就来和大家聊聊公众号文章怎么写才能提高阅读量&#xff1a; 一、 内容干货满足读者求知欲 只要你的文章实用性强&…

LeetCode(31) 下一个排列

整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如&#xff0c;arr [1,2,3] &#xff0c;以下这些都可以视作 arr 的排列&#xff1a;[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。 整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地&#xf…

多线程基础入门【Linux】——下篇

目录 一&#xff0c;死锁 1. 死锁的必要条件 2&#xff0c;避免死锁 二&#xff0c;条件变量 同步概念与竞态条件 条件变量——初始化 静态初始化 动态初始化 pthread_cond_destroy (销毁) pthread_cond_wait (等待条件满足) pthread_cond_signal (唤醒线程) ph…

【AI】LoFTR图像匹配算法源码解析

0.LoFTR简介 Local Feature Transformers &#xff08;LoFTR&#xff09;是一种Detector-free的局部特征匹配方法&#xff0c;使用了具有自注意层和互注意层的Transformer模块来处理从卷积网络中提取的密集局部特征&#xff1a;首先在低特征分辨率&#xff08;图像维度的1/8&a…

mysql的读写分离

MySQL 读写分离原理 读写分离就是只在主服务器上写&#xff0c;只在从服务器上读。 主数据库处理事务性操作&#xff0c;而从数据库处理 select 查询。 数据库复制被用来把主数据库上事务性操作导致的变更同步到集群中的从数据库。 常见的mysql读写分离分为以下两种 1&…