【算法与数据结构】队列的实现详解

news2024/11/18 3:30:30

请添加图片描述

文章目录

  • 📝队列的概念及结构
  • 🌠 队列的顺序实现
      • 🌉初始化
      • 🌠入队
      • 🌉出队
      • 🌠获取队列首元素
      • 🌉获取队列尾部元素
      • 🌠获取队列中有效元素个数
      • 🌉 队列是否为空
      • 🌠查看队列是否为满
      • 🌉销毁队列
    • 🌠测试
  • 🌉 顺序队列的假溢出
  • 🌠循环队列概念
      • 🌉循环队列的初始化
      • 🌠循环队列的入队操作
      • 🌉 循环队列的出队操作
      • 🌉 查看队首元素
      • 🌠查看队尾元素
      • 🌉查看队列是否为空
      • 🌠查看队列是否已满
    • 🌉 测试下循环队列
  • 🚩总结


📝队列的概念及结构

  1. 队列的概念:
    队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 新添加的元素添加到队尾,只能从队头取出元素。
    入队列:进行插入操作的一端称为队尾
    出队列:进行删除操作的一端称为队头
    在这里插入图片描述
  2. 队列特征如下:

入队(Enqueue):通过尾指针添加元素到队列尾部,即向队列中插入元素。

出队(Dequeue):通过头指针删除队列头部元素,即从队列中移除元素。

空队列:当头指针和尾指针相同时,表示队列为空。

满队列:当尾指针指向队列容量最大位置时,表示队列已满。

  1. 常用的队列结构包括数组和链表实现:

数组实现队列:使用一维数组存储元素,头指针和尾指针分别指向数组的下标位置。

链表实现队列:每个元素使用一个节点存储,头节点和尾节点通过指针链接实现队列。

🌠 队列的顺序实现

头文件:Queue_order.h

# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

typedef int QDataType;

typedef struct Queue
{
	QDataType data[MAX_SIZE];
	int front;
	int rear;
	int size;//记录队列中元素数量
}Queue;

//初始化队列
void InitQueue(Queue* pq);
//入队操作
void EnQueue(Queue* pq,int value);
//出队
QDataType DeQueue(Queue* pq);
//获取队首元素
QDataType FrontQueue(Queue* pq);
//获取队尾元素
QDataType RearQueue(Queue* pq);

//获取队列中有效元素个数
QDataType SizeQueue(Queue* pq);
//队列是否为空
QDataType IsEmpty(Queue* pq);
//队列是否为满
QDataType IsFull(Queue* pq);
//销毁队列
void DestroyQueue(Queue* pq);

🌉初始化

在这里插入图片描述

//初始化队列
void InitQueue(Queue* pq)
{
	pq->front = -1;
	pq->rear = -1;
	pq->size = 0;
}

front和rear都指向-1,表示队列中没有数据。size为0,表示队列中没有元素。

🌠入队

//入队
void EnQueue(Queue* pq, int value)
{
	if (pq->front == MAX_SIZE - 1)
	{
		printf("队列满了,入队操作失败");//检查队列是否已满,如果front指针指向的下一个位置就是MAX_SIZE-1,表示队列已满,打印错误信息并返回。
		return;
	}
	if (pq->front == -1)
	{//如果front为-1,表示队列为空,将front指针置0。
		pq->front = 0;
	}
	pq->rear++;//如果front为-1
	pq->data[pq->rear] = value;//表示队列为空
	pq->size++;//加完了别忘了size++
}

🌉出队

//出队
QDataType DeQueue(Queue* pq)
{
	if (-1 == IsEmpty)
	{
		printf("队列为空!");
		return -1;//返回一个特定的值表示队列为空
	}
	int value = pq->data[pq->front];
	pq->front++;
	pq->size--;
	return value;
}

在这里插入图片描述
检查满是为了防止入队越界,front-1时,表示队列为空,需要将front置0,rear后移一位指向新的元素位置,将元素值写入data数组,size计数增加。

🌠获取队列首元素

//获取队首元素
QDataType FrontQueue(Queue* pq)
{
	if (-1 == IsEmpty)
	{
		printf("队列为空!");
		return -1;//返回一个特定的值表示队列为空
	}
	return pq->data[pq->front];
}

🌉获取队列尾部元素

//获取队列尾部元素
QDataType RearQueue(Queue* pq)
{
	if (0 == IsEmpty)
	{
		printf("队列为空!");
		return -1;//返回一个特定的值表示队列为空
	}
	return pq->data[pq->rear];
}

🌠获取队列中有效元素个数

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

🌉 队列是否为空

//队列是否为空
QDataType IsEmpty(Queue* pq)
{
	if (pq->front == -1 || (pq->front > pq->rear))
	{
		//队列为空
		return -1;
	}
	else
	{
		//队列不为空
		return 1;
	}
	
}

注意:在队列中,如果 front 指针大于 rear 指针,表示队列为空。这是因为 front 指针指向队列的第一个元素,而 rear 指针指向队列的最后一个元素。如果 front 指针大于 rear 指针,意味着队列中没有元素,或者已经出队了所有的元素。
考虑一个队列中有一个元素的情况。初始时 front 和 rear 都指向该元素,而当该元素出队时,front 指针会被移动到下一个位置,这时 front 指针就比 rear 指针大,表示队列已经为空。

🌠查看队列是否为满

//查看队列是否为满
QDataType IsFull(Queue* pq)
{
	return (pq->rear == MAX_SIZE - 1);
}

🌉销毁队列

//销毁队列
void DestroyQueue(Queue* pq)
{
	InitQueue(pq);//重新初始化队列,清空元素并释放内存
}

🌠测试

源文件Test.c

# define _CRT_SECURE_NO_WARNINGS 1
#include"Queue_order.h"

int main()
{
	Queue Pq;
	InitQueue(&Pq);

	EnQueue(&Pq, 10);
	EnQueue(&Pq, 20);
	EnQueue(&Pq, 30);
	
	printf("Front element: %d\n", FrontQueue(&Pq));
	printf("Rear element: %d\n", RearQueue(&Pq));
	printf("Front element: %d\n", SizeQueue(&Pq));
	printf("Front element: %d\n", FrontQueue(&Pq));

	printf("Dequeued element: %d\n", DeQueue(&Pq));
	printf("Dequeued element: %d\n", DeQueue(&Pq));
	printf("Dequeued element: %d\n", DeQueue(&Pq));
	printf("Dequeued element: %d\n", DeQueue(&Pq)); // 尝试从空队列中出队

	printf("Queue size: %d\n", SizeQueue(&Pq));
	printf("Is queue empty? %s\n", IsEmpty(&Pq) ? "Yes" : "No");
}

在这里插入图片描述

🌉 顺序队列的假溢出

顺序队列的假溢出指的是,队列在结构上没有真正满溢,但是在逻辑上已经无法再插入新元素了。
在队尾指针已经指向数组的最后一个位置,但数组中仍然有空闲空间时,确实是队列溢出的情况,而不是假溢出。这种情况通常是由于没有充分利用队列所分配的存储空间所导致的,因此会造成队列操作的限制。

“假溢出” 通常用于表示队列中还有空闲空间,但因某种原因无法继续插入元素的情况,这可能是由于某些限制条件或错误的队列操作所导致的。例如,可能存在对队列的错误管理,使得无法正确地判断队列是否已满,导致了插入元素失败的情况。
举个例子:
在这里插入图片描述

在一个顺序队列中,队列大小为5,已包含四个元素 value1-2-3-4,rear在队尾4的位置。
当出队两个元素value1-2时,队列前面多出来了两位置
在这里插入图片描述
当你想再插入来两个数据时,队列确只能插入一个数据,但是队列其实不能插入数据了。
这是队列在结构上没有真正满溢,但是在逻辑上已经无法再插入新元素了。
在这里插入图片描述
怎么优化这个假溢出呢?两种常见的方法:

  1. 循环队列: 循环队列是一种特殊的顺序队列,通过将队列的数组视为一个循环的环形结构,使得在队列尾部插入元素时可以利用数组头部的空闲空间,从而解决了假溢出的问题。循环队列中,当队尾指针指向数组的末尾时,再插入元素时将其指向数组的起始位置,这样就形成了一个循环。通过这种方式,可以充分利用数组空间,避免了假溢出。
  2. 动态扩容: 动态扩容是在顺序队列满时,自动增加数组的大小以容纳更多元素。当队列满时,分配一个更大的数组,并将原有的元素复制到新数组中,然后释放原来的数组。这样可以确保队列始终有足够的空间来插入新的元素,从而避免假溢出。动态扩容的关键是选择适当的扩容策略,以及控制扩容时机,以避免频繁的扩容操作带来的性能开销。

🌠循环队列概念

循环队列是一种基于数组实现的队列数据结构,其特点是通过循环利用数组空间来实现队列的操作。循环队列的数组通常被看作一个环形的结构,队列的头部和尾部指针在数组中循环移动,使得当尾部指针到达数组末尾时,可以将其“循环”到数组的起始位置,从而避免了溢出或假溢出的情况。
在这里插入图片描述

循环队列看似循环,其实是固定数组不断往复的过程,我们可以模拟普通数组来实现:
在这里插入图片描述
如图:data 表示一个数据域,int 为类型,当然你也可以修改为任意自定义的类型,也可以是复杂的结构体类型。
MAX_SIZE :表示循环最大容量,可进行放数据的操作空间
rear代表尾指针,入队时移动。
front代表头指针,出队时移动。
size记录当前有效数据的多少

代码:

#define MAX_SIZE 5

typedef struct {
    int data[MAX_SIZE];
    int front; // 队列头指针
    int rear;  // 队列尾指针
    int size;  // 队列当前元素个数
} Cir_Queue;

🌉循环队列的初始化

对于循环队列来说,front从0开始是合理的,因为数据数组是环状结构,front从0开始可以实现队列元素的循环利用。
rear从0开始表示队列此时为空,front和rear指针都指向数组第一个位置。
将队列当前元素个数size清零,表示队列为空。

// 初始化队列
void initQueue(Cir_Queue* queue) 
{
    queue->front = 0;将队列头指针front设置为0。
    queue->rear = 0;将队列尾指针rear也设置为0。
    queue->size = 0;
}

🌠循环队列的入队操作

入队操作与顺序队列相同,只需将rear向后移动。但要注意,如果rear达到队列的上限,需从头开始移动。建议使用余数法,确保操作在队列空间内进行,避免一次错误导致整体崩溃。
在这里插入图片描述
要进行入队操作,得先判断队列是不是满了rear==front来判断队空,也可以用size。

// 入队操作
void enqueue(Cir_Queue* queue, int value) 
{
    if (queue->size == MAX_SIZE) 
    {
        printf("Queue is full. Enqueue operation failed.\n");
        return;
    }
    queue->data[queue->rear] = value;
    queue->rear = (queue->rear + 1) % MAX_SIZE; // 使用(rear+1)%MAX_SIZE更新rear指针,循环移动
    queue->size++;
}

检查队列是否已满,可通过判断size是否等于MAX_SIZE来确定。如果已满,则打印提示信息并返回;将数值value赋给data数组的rear索引位置,并使用(rear+1)%MAX_SIZE更新rear指针。这里使用模运算来实现rear指针的循环移动。当rear指向最后一个位置时,利用模运算使其指向第一个位置,实现循环利用数组。然后将size增加1,表示元素个数加1。

🌉 循环队列的出队操作

// 出队操作
int dequeue(Cir_Queue* queue)
 {
    if (queue->size == 0)
     {
        printf("Queue is empty. Dequeue operation failed.\n");
        return -1;
    }
    int value = queue->data[queue->front];
    queue->front = (queue->front + 1) % MAX_SIZE; // 循环移动
    queue->size--;
    return value;
}

使用(front+1)%MAX_SIZE更新front指针,这里也使用模运算实现front指针的循环移动。当front指向最后一个位置时,利用模运算让它指向第一个位置。

🌉 查看队首元素

// 查看队首元素
int front(Cir_Queue* queue) 
{
    if (queue->size == 0) 
    {
        printf("Queue is empty. Front operation failed.\n");
        return -1;
    }
    return queue->data[queue->front];
}

🌠查看队尾元素

// 查看队尾元素
int rear(Cir_Queue* queue) 
{
    if (queue->size == 0) 
    {
        printf("Queue is empty. Rear operation failed.\n");
        return -1;
    }
    return queue->data[(queue->rear - 1 + MAX_SIZE) % MAX_SIZE];
}

🌉查看队列是否为空

// 查看队列是否为空
int isEmpty(Cir_Queue* queue) 
{
    return (queue->size == 0);
}

🌠查看队列是否已满

// 查看队列是否已满
int isFull(Cir_Queue* queue) 
{
    return (queue->size == MAX_SIZE);
}

🌉 测试下循环队列

int main() {
    Cir_Queue queue;
    initQueue(&queue);

   
    enqueue(&queue, 10);
    enqueue(&queue, 20);
    enqueue(&queue, 30);
    enqueue(&queue, 40);
    

    printf("Front element: %d\n", front(&queue));

    printf("Dequeued element: %d\n", dequeue(&queue));
    printf("Dequeued element: %d\n", dequeue(&queue));
    printf("Dequeued element: %d\n", dequeue(&queue));
    enqueue(&queue, 80);
    enqueue(&queue, 90);
    printf("Front element: %d\n", front(&queue));
    printf("rear element: %d\n", rear(&queue));

    return 0;
}

在这里插入图片描述


🚩总结

感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘

请添加图片描述

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

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

相关文章

二分查找的理解及应用场景。

一、是什么 在计算机科学中&#xff0c;二分查找算法&#xff0c;也称折半搜索算法&#xff0c;是一种在有序数组中查找某一特定元素的搜索算法 想要应用二分查找法&#xff0c;则这一堆数应有如下特性&#xff1a; 存储在数组中有序排序 搜索过程从数组的中间元素开始&…

【典】dp背包问题(树求方案)

回顾在acw上做过的题 有依赖的背包问题 这一题是与树相关的dp问题&#xff0c;根据父节点与子节点的相连关系&#xff0c;我们用dfs来处理根节点与子树的迭代更新&#xff0c;把每一颗最小单位子树看成一个物品&#xff0c;然后就有点像多重背包&#xff08;因为有体积限制&…

云计算 3月12号 (PEX)

什么是PXE&#xff1f; PXE&#xff0c;全名Pre-boot Execution Environment&#xff0c;预启动执行环境&#xff1b; 通过网络接口启动计算机&#xff0c;不依赖本地存储设备&#xff08;如硬盘&#xff09;或本地已安装的操作系统&#xff1b; 由Intel和Systemsoft公司于199…

【vue在主页中点击主页面如何弹出一个指定某个页面的窗口】

【vue在主页中点击主页面跳转到某个页面的操作完整过程】 1.首先在主页面中加入一个卡槽用于展示弹出的窗口 代码如下&#xff1a; <el-dialog :visible.sync"dialogVisible1" :close-on-click-modal"false" :title"title" class"dial…

关于tcp协议

目录 前言&#xff1a; 一、TCP协议的基本概念&#xff1a; 二、TCP协议的主要特点&#xff1a; 2.1面向连接&#xff1a; 2.2可靠传输&#xff1a; 2.3基于字节流&#xff1a; 三、TCP连接的建立与终止&#xff1a; 3.1连接建立&#xff1a; 3.1.1SYN&#xff1a; 3…

资产管理系统建设方案参考

1系统概述 软件开发全套文档下载、源码下载&#xff1a;软件项目开发全套文档下载_软件开发文档下载-CSDN博客 1.1需求描述 1. 实现公司内部固定资产管理全生命周期管理&#xff0c;包括资产采购、资产入库、资产领用、资产借用、资产归还、资产报废、资产维修、资产调拨等全…

RC522刷卡电路设计及程序

一、RC522刷卡电路组成 基于RC522的刷卡电路如上图所示。该电路组成主要分为三部分&#xff1a; Receiving Circuit&#xff1a;接收电路&#xff0c;接收卡发送的数据。 Filtering Impedence-Transtorm circuit:滤波和阻抗变换电路&#xff0c;抑制高次谐波并优化到读卡器天线…

Python使用lxml解析XML格式化数据

Python使用lxml解析XML格式化数据 1. 效果图2. 源代码参考 方法一&#xff1a;无脑读取文件&#xff0c;遇到有关键词的行再去解析获取值 方法二&#xff1a;利用lxml等库&#xff0c;解析格式化数据&#xff0c;批量获取标签及其值 这篇博客介绍第2种办法&#xff0c;以菜鸟教…

【AI大模型应用开发】【LangChain系列】9. 实用技巧:大模型的流式输出在 OpenAI 和 LangChain 中的使用

大家好&#xff0c;我是同学小张&#xff0c;日常分享AI知识和实战案例欢迎 点赞 关注 &#x1f44f;&#xff0c;持续学习&#xff0c;持续干货输出。v: jasper_8017 一起交流&#x1f4ac;&#xff0c;一起进步&#x1f4aa;。微信公众号也可搜【同学小张】 &#x1f64f; 本…

Linux环境(Ubuntu)上搭建MQTT服务器(EMQX )

目录 概述 1 认识EMQX 1.1 EMQX 简介 1.2 EMQX 版本类型 2 Ubuntu搭建EMQX 平台 2.1 下载和安装 2.1.1 下载 2.1.2 安装 2.2 查看运行端口 3 运行Dashboard 管理控制台 3.1 查看Ubuntu上的防火墙 3.2 运行Dashboard 管理控制台 概述 本文主要介绍EMQX 的一些内容&a…

Vulnhub - DevGuru

希望和各位大佬一起学习&#xff0c;如果文章内容有错请多多指正&#xff0c;谢谢&#xff01; 个人博客链接&#xff1a;CH4SER的个人BLOG – Welcome To Ch4sers Blog DevGuru 靶机下载地址&#xff1a;DevGuru: 1 ~ VulnHub 目录 0x01 信息收集 0x02 Web漏洞利用 - W…

GPT-4.5 Turbo意外曝光,最快明天发布?OpenAI终于要放大招了!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

什么是信创?什么是信创测评?

信创&#xff0c;全称为信息技术应用创新&#xff0c;我的理解&#xff0c;其核心是知识产权与供应链的自主可控与可持续发展。 我国在经历了无数风雨后&#xff0c;已经浴火重生&#xff0c;国家实力日益强大&#xff0c;人民生活水平也不断提高。然而&#xff0c;我们必须清…

彩虹知识付费模板MangoA全开源包含秒杀/抽奖/社群/推送等功能

二次开发增加以下功能每日秒杀每日签到官方社群多级分销在线抽奖项目投稿 每日秒杀 每日签到 官方社群 多级分销 在线抽奖 项目投稿 下载地址&#xff1a;https://pan.xunlei.com/s/VNstMfOecGliiqew7UIorsOnA1?pwdhywi#

2024年云仓酒庄:店中店增项新模式,开启葡萄酒文化新篇章

2024云仓酒庄&#xff1a;店中店增项新模式&#xff0c;开启葡萄酒文化新篇章 在葡萄酒行业蓬勃发展的今天&#xff0c;云仓酒庄以其独特的经营模式和创新思维&#xff0c;在市场中脱颖而出。2024年&#xff0c;云仓酒庄继续深化其战略布局&#xff0c;不仅在多地开设酒庄实体…

微信小程序开发系列(三十二)·如何通过小程序的API实现页面的上拉加载(onReachBottom事件)和下拉刷新(onPullDownRefresh事件)

目录 1. 上拉加载 2. 下拉刷新 1. 上拉加载 上拉加载是小程序中常见的一种加载方式&#xff0c;当用户滑动页面到底部时&#xff0c;会自动加载更多的内容&#xff0c;以便用户继续浏览小程序中实现上拉加载的方式&#xff1a; ① 在app.json或者page.json中配置距离页面…

比特币创造历史新纪录

综合来源&#xff1a;coindesk and cointelegraph 编译&#xff1a;秦晋 3月11日&#xff0c;比特币在亚洲交易时段首次突破71,000美元&#xff0c;这个是比特币创造的价格新纪录。自1月11日比特币现货ETF在美国获批以来&#xff0c;比特币一直在稳步上涨。以太币突破4000美元。…

JAVA基础—集合详细解析

1.JAVA中的4种集合类型 Set&#xff1a;无序、不可重复的集合List&#xff1a;有序、重复的集合Queue&#xff1a; 队列Map&#xff1a;映射关系 首先&#xff0c;JAVA的集合类主要有两个接口派生而出&#xff1a;Collection和Map。Collection和Map就是JAVA集合的根接口。所以后…

【目标检测经典算法】R-CNN、Fast R-CNN和Faster R-CNN详解系列一:R-CNN图文详解

学习视频&#xff1a;Faster-RCNN理论合集 概念辨析 在目标检测中&#xff0c;proposals和anchors都是用于生成候选区域的概念&#xff0c;但它们在实现上有些许不同。 Anchors&#xff08;锚框&#xff09;&#xff1a; 锚框是在图像中预定义的一组框&#xff0c;它们通常以…

代码随想录算法训练营Day45 ||leetCode 70. 爬楼梯 (进阶)|| 322. 零钱兑换 || 279.完全平方数

70. 爬楼梯 &#xff08;进阶&#xff09; 本质上和leetcode377一样 #include <iostream> #include <vector> using namespace std; int main() {int n, m;while (cin >> n >> m) {vector<int> dp(n 1, 0);dp[0] 1;for (int i 1; i < n; i…