【数据结构】详解队列和循环队列

news2025/8/16 3:32:04

目录

  • 一.队列
    • 1.队列的概念及结构
    • 2.队列的实现
      • Queue.h
      • Queue.c
  • 二.循环队列
    • 1.循环队列的实现
    • 2.设计循环队列
      • 解题思路
      • 代码

一.队列

1.队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First in first out)的特性
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
在这里插入图片描述

2.队列的实现

队列也可以用数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,在数组头部出队列,效率会很低。
在这里插入图片描述

  • 如果队列使用顺序表的结构实现,进行出队操作时,有两种方法,1是将对头指针指向下一个位置,这会造成空间上的浪费。
    2是将对头后的所有数据向前移动一个位置,出队操作的时间复杂度就会变大,所以使用顺序表实现队列是不可取的。
  • 因为队列要对对头和队尾进行操作,所以我们需要使用两个结构体指针,分别指向队头和队尾。

Queue.h

存放队列的所有函数的声明

#pragma once
#include<stdio.h>
#include<assert.h>

typedef int QDataType;

typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	QNode* front;
	QNode* tail;
	int size;
}Queue;

void QueueInit(Queue* q);//初始化队列

void QueuePush(Queue* q, QDataType x);//队尾入队列

void QueuePop(Queue* q);//对头出队列

QDataType QueueFront(Queue* q);//获取队列头部元素

QDataType QueueTail(Queue* q);//获取队队尾元素

int QueueSize(Queue* q);//查看队列中有效元素的个数

int QueueEmpty(Queue* q);//查看队列是否为空,若是非空返回非零结果,若是为空,返回0

void QueueDestroy(Queue* q);//销毁队列

void QueuePrint(Queue* p);//打印队列

Queue.c

存放所有函数的实现

1.初始化队列

//初始化队列
void QueueInit(Queue* q)
{
	q->front = NULL;
	q->tail = NULL;
	q->size = 0;
}

将对头和队尾全部置为空,当进行入队操作时,在使这两个指针分别指向对应的空间。

2.队尾入队列

//队尾入队列
void QueuePush(Queue* q, QDataType x)
{
	assert(q);

	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (!newNode)
	{
		perror("malloc fail");
		exit(-1);
	}
	newNode->data = x;
	newNode->next = NULL;

	if (!q->front)
	{
		q->front = q->tail = newNode;
	}
	else
	{
		q->tail->next = newNode;
		q->tail = newNode;
	}
	q->size++;
}

向内存申请一个存放数据的空间,并向其存放数据。
判断此时队列中是否有数据,如果没有,对头和队尾都指向申请的空间,如果有,将队尾指向的空间的next赋值为新申请的空间,在将队尾指向新申请的空间。
最后将队列的大小加1,完成入队操作

3.队头出队列

//对头出队列
void QueuePop(Queue* q)
{
	assert(q);
	//当队列中没有元素时
	assert(q->front);

	if (q->front == q->tail)
	{
		q->front = q->tail = NULL;
	}
	else
	{
		QNode* newNode = q->front;
		q->front = q->front->next;
		free(newNode);
	}
	q->size--;
}

首先判断,队列是否为空,若为空,无法进行出队列操作。
其次判断队列是否只要一个数据,若对头和队尾只写同一个空间,即只有一个数据,此时将两个指针都置为空,完成出队列操作。
如果,队列不为空,且队列中有多个数据,则队头进行操作,将对头指向的下一个位置变为对头,将之前的位置释放(队列中的空间是向内存申请的,需要释放,否则会造成内存泄漏),完成出队操作。

4.获取对头数据

//获取队列头部元素
QDataType QueueFront(Queue* q)
{
	assert(q);//查看队列是否为空
	assert(q->front);//查看队列头部是否为空

	return q->front->data;
}

当队列不为空时,返回对头指向的空间所存储的数据即可。

5.获取队尾数据

//获取队队尾元素
QDataType QueueTail(Queue* q)
{
	assert(q);
	assert(q->front);

	return q->tail->data;
}

若队列不为空,返回队尾指针指向的空间所存储的数据。

6.查看队列中有效元素的个数

//查看队列中有效元素的个数
int QueueSize(Queue* q)
{
	assert(q);
	assert(q->front);

	//QNode* cur = q->front;
	//int size = 0;

	//while (cur)
	//{
	//	size++;
	//	cur = cur->next;
	//}
	//return size;

	return q->size;
}

有两种方法,
方法1:我们在设置队列的结构体时,定义了队列的长度size,此时返回size的大小即可。
方法2:若没有定义,则需要我们遍历队列,判断队列的长度
在实际应用队列时,我们不知道队列的结构体是如何设置的,我们只能通过这个函数来获取队列的有效元素的个数。

7.查看队列是否为空

//查看队列是否为空,若是非空返回非零结果,若是为空,返回0
int QueueEmpty(Queue* q)
{
	assert(q);
	
	return q->front == NULL && q->tail == NULL;
}

8.销毁队列

//销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);

	QNode* cur = q->front;

	while (cur)
	{
		QNode* temp = cur;
		cur = cur->next;
		free(temp);
	}

	q->front = q->tail = NULL;
	q->size = 0;
}

队列中的空间都是向内存申请的,需要自己主动去释放,将队头的地址赋给一个临时的指针,由该指针去释放,当该指针为空时,表示已释放完毕。
最后我们将对头和队尾置空,将队列的大小改为0即可。

9.Queue.c完整代码

#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"

//初始化队列
void QueueInit(Queue* q)
{
	q->front = NULL;
	q->tail = NULL;
	q->size = 0;
}

//队尾入队列
void QueuePush(Queue* q, QDataType x)
{
	assert(q);

	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (!newNode)
	{
		perror("malloc fail");
		exit(-1);
	}
	newNode->data = x;
	newNode->next = NULL;

	if (!q->front)
	{
		q->front = q->tail = newNode;
	}
	else
	{
		q->tail->next = newNode;
		q->tail = newNode;
	}
	q->size++;
}

//对头出队列
void QueuePop(Queue* q)
{
	assert(q);
	//当队列中没有元素时
	assert(q->front);

	if (q->front == q->tail)
	{
		q->front = q->tail = NULL;
	}
	else
	{
		QNode* newNode = q->front;
		q->front = q->front->next;
		free(newNode);
	}
	q->size--;
}

//获取队列头部元素
QDataType QueueFront(Queue* q)
{
	assert(q);//查看队列是否为空
	assert(q->front);//查看队列头部是否为空

	return q->front->data;
}

//获取队队尾元素
QDataType QueueTail(Queue* q)
{
	assert(q);
	assert(q->front);

	return q->tail->data;
}

//查看队列中有效元素的个数
int QueueSize(Queue* q)
{
	assert(q);
	assert(q->front);

	//QNode* cur = q->front;
	//int size = 0;

	//while (cur)
	//{
	//	size++;
	//	cur = cur->next;
	//}
	//return size;

	return q->size;
}

//查看队列是否为空,若是非空返回非零结果,若是为空,返回0
int QueueEmpty(Queue* q)
{
	assert(q);
	
	return q->front == NULL && q->tail == NULL;
}

//销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);

	QNode* cur = q->front;

	while (cur)
	{
		QNode* temp = cur;
		cur = cur->next;
		free(temp);
	}

	q->front = q->tail = NULL;
	q->size = 0;
}

//打印队列
void QueuePrint(Queue* p)
{
	assert(p);
	QNode* cur = p->front;
	while (cur)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

二.循环队列

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模式时可以使用循环队列。
循环队列可以使用数组实现,也可以使用循环链表实现。
这里我们使用数组实现,因为一般循环队列的长度是固定的,数组可以很好的契合这一标准。
在这里插入图片描述

1.循环队列的实现

环形队列可以用数组实现,也可以用循环链表实现。
根据不同的情况,我们选择不同的实现方法:
当给定的循环链表的大小是固定时,使用数组更为简单,可以少掉链表的插入和删除,直接根据下标解决问题。
当给定的循环链表的大小不是固定时,可以试着使用循环链表,也可以少掉很多麻烦。

  • 这里我们根据Leetcode上的一道循环链表题来讲解这个知识点。

2.设计循环队列

设计循环队列
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

解题思路

该题中,循环队列给定了固定的大小,我们就可以使用顺序表实现它。
首先给出的数组大小为7个整形:
image.png
当队列为空时,我们将对头和队尾都指向下标为0的位置,
当插入元素时,我们将对头和队尾移动到下标为1~7的位置
image.png
这题的难点就是对队列入队和出队的操作,解决这两个问题,其他的在这两个的基础上也就迎刃而解。
这时我们就要分情况讨论了,

  • 当队列已满时,有这几种情况
    image.png
    所以当我们判断队列是否已满,或进行入队操作时,可以根据这两种情况判断
  • 当进行出队操作时,又有这两种特殊种情况
    image.png
    情况1:出队后队尾的位置加1等于对头的位置,表明此时队列为空,将对头和队尾置为0,表示此时队列为空
    情况2:出队后对头的位置大于所给出队列的大小,需将队头位置改为1
    其他出队情况直接将对头位置加1即可
    image.png

代码

typedef struct {
    int* arr;
    int head;
    int rear;
    int size;
} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if(!obj)
    {
        perror("malloc fail!");
        exit(-1);
    }
    obj->arr = (int*)malloc(sizeof(int)*(k+1));
    obj->head = obj->rear = 0;
    obj->size = k;
    return obj;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    assert(obj);

    if(obj->head == 0)
    {
        obj->arr[++obj->head] = value;
        obj->rear++;
        return true;
    }
    else if(obj->rear < obj->size && (obj->rear+1 < obj->head || obj->rear >= obj->head))
    {
        obj->arr[++obj->rear] = value;
        return true;
    }
    else if(obj->rear == obj->size && obj->head > 1)
    {
        obj->arr[1] = value;
        obj->rear = 1;
        return true;
    }
    return false;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    assert(obj);

    if(obj->head)
    {
        obj->head++;
        if(obj->head == obj->rear+1)
            obj->head = obj->rear = 0;
        else if(obj->head > obj->size)
            obj->head = 1;
        return true;
    }
    return false;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    assert(obj);
    if(!myCircularQueueIsEmpty(obj))
        return obj->arr[obj->head];
    return -1;
}

int myCircularQueueRear(MyCircularQueue* obj) {
    assert(obj);
    if(!myCircularQueueIsEmpty(obj))
        return obj->arr[obj->rear];
    return -1;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    assert(obj);
    if(obj->head == 0)
        return true;
    return false;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    assert(obj);
    if(obj->rear+1 == obj->head)
        return true;
    if(obj->rear == obj->size && obj->head == 1)
        return true;
    return false;
}

void myCircularQueueFree(MyCircularQueue* obj) {
    assert(obj);
    free(obj->arr);
    obj->arr = NULL;
    obj->head = obj->rear = obj->size = 0;
}

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);
 
 * bool param_2 = myCircularQueueDeQueue(obj);
 
 * int param_3 = myCircularQueueFront(obj);
 
 * int param_4 = myCircularQueueRear(obj);
 
 * bool param_5 = myCircularQueueIsEmpty(obj);
 
 * bool param_6 = myCircularQueueIsFull(obj);
 
 * myCircularQueueFree(obj);
*/

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

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

相关文章

四、网络层(六)移动IP

目录 6.1 移动IP的概念 6.2 移动IP的基本工作原理 6.2.1代理发现与注册 6.2.2固定主机向移动主机发送IP数据报 6.2.3移动主机向固定主机发送IP数据报 6.2.4同址转交地址&#xff08;简单了解&#xff09; 6.2.5三角形路由问题&#xff08;简单了解&#xff09; 6.1 移…

事关你“吃住行游购娱”的12项安全国标图解来了

标准是安全建设的“尺子”。近期&#xff0c;国家市场监督管理总局、国家标准化管理委员会发布中华人民共和国国家标准公告&#xff08;2022年第13号&#xff09;&#xff0c;全国信息安全标准化技术委员会归口的14项网络安全国家标准获批发布&#xff0c;其中12项涉及数据安全…

不同类型单板布线策略6大类

类型一PCB布线策略 一 &#xff0c;类型一主要特征如下&#xff1a; 严格的长度规则、严格的串扰规则、拓扑规则、差分规则、电源地规则等。 二&#xff0c;关键网络的处理&#xff1a;总线定义Class&#xff1b; 要求满足一定的拓扑结构、stub及其长度&#xff08;时域&a…

关于模型中的R方

1、一元线性回归 R方在一元线性回归模型中&#xff0c;衡量【响应变量X和预测变量Y】的线性关系。 R方cor&#xff08;X,Y)^2 但是&#xff0c;在多元线性回归模型中&#xff0c;因为涉及多个预测变量&#xff0c;全部R方就是衡量响应变量和多个预测变量当中的关系。 而有关…

阿里云张献涛:高性能计算发展的三大趋势

12 月 12-15 日&#xff0c;第十八届 CCF 全国高性能计算学术年会&#xff08;以下简称 CCF HPC China 2022&#xff09;以线上的方式举行&#xff0c;国内外众多知名专家学者&#xff0c;以及高性能计算产业界的头部企业代表云上相聚&#xff0c;探讨高性能计算的发展趋势。阿…

zabbix6.0安装教程(五):二进制包安装

zabbix6.0安装教程&#xff08;五&#xff09;&#xff1a;二进制包安装 目录一、使用ZABBIX官方存储库二、Red Hat zabbix企业版 Linux/CentOS1. 概述2. 安装注意事项2.1 使用 Timescale DB 导入数据2.2 PHP 7.22.3 配置 SELinux3. Proxy 安装3.1 创建数据库3.2 导入数据3.3 为…

计算机网络原理第5章 运输层(12.24完结)

目录~ 5.1 运输层协议概述 5.1.1 进程之间的通信 从通信和信息处理的角度看&#xff0c;运输层向它上面的应用层提供通信服务&#xff0c;它属于面向通信部分的最高层&#xff0c;同时也是用户功能中的最低层。 当网络的边缘部分中的两个主机使用网络的核心部分的功能进行…

再学C语言12:字符串(3)——转换说明

一、转换说明的意义 意义&#xff1a;把存储在计算机中的二进制格式的数值转换成一系列字符&#xff08;一个字符串&#xff09;以便于显示&#xff1b;实质上是翻译说明&#xff0c;并不会替代原值 应该使转换说明与要打印的值的类型相匹配 参数传递机制 float n1; double …

陈都灵现身海南国际电影节,新片《关索岭》票房有望超《阿凡达》

刚送走了厦门金鸡奖&#xff0c;又迎来了海南电影节&#xff0c;第四届国际电影节&#xff0c;已经在美丽的海南岛拉开帷幕。 众多的中国优秀电影人&#xff0c;都欢聚一堂共话未来&#xff0c;为中国电影的发展献言献策&#xff0c;也展现出电影人的精神风貌。 在本届电影节走…

WMS系统这么重要?一文教你找到理想中的WMS系统

无论是在线上还是线下&#xff0c;相信大家都见过各式各样的仓库&#xff0c;或杂乱或整洁&#xff0c;有的还在使用传统的纸单作业模式&#xff0c;有的已经进入全自动化无人作业模式。然而&#xff0c;随着仓储物流行业竞争愈发激烈&#xff0c;以及数智化转型浪潮席卷而来&a…

python中logging模块的一些简单用法

用Python写代码的时候&#xff0c;在想看的地方写个print xx 就能在控制台上显示打印信息&#xff0c;这样子就能知道它是什么了&#xff0c;但是当我需要看大量的地方或者在一个文件中查看的时候&#xff0c;这时候print就不大方便了&#xff0c;所以Python引入了logging模块来…

小学生C++编程基础 课程8(B)

919.3数排序 ( 课程8) 登录 920.求最小值 (课程8) 登录 921.排名 (课程8) 登录 922.中间数 ( 课程8&#xff09; 难度&#xff1a;1 登录 923.判断闰年 &#xff08;课程8&#xff09; 难度&#xff1a;1 登录 924.天数 (课程8) 难度&#xff1a;1 登录 《小学生C趣味编程…

Splunk Enterprise 存在任意代码执行漏洞

漏洞描述 Splunk 是一款机器数据的引擎&#xff0c;可用于收集、索引和利用所有应用程序、服务器和设备生成的快速移动型计算机数据 。 Splunk 受影响版本存在任意代码执行漏洞&#xff0c;经过身份验证的攻击者可利用此漏洞通过创建包含恶意代码的 SimpleXML 仪表板&#xf…

说话人识别中的损失函数

损失函数 损失函数L(yi,y^i)L(y_i,\hat{y}_i)L(yi​,y^​i​)用来描述神经网络的输出y^i\hat{y}_iy^​i​和基本事实&#xff08;Ground Truth&#xff0c;GT&#xff09;yiy_iyi​的差异对于回归问题&#xff0c;常用均方误差&#xff08;Mean Square Error&#xff0c;MSE&a…

IB成绩该如何换算GPA?

今天就简单介绍下IB课程分数与GPA以及英国的UCAS Tariff points的分数换算&#xff01; 网传的换算方法是这样的&#xff1a; 以IB单科满分是7分为前提&#xff0c;那么把IB成绩换算成四分制的GPA的方式是&#xff1a;将IB课程分数除以7再乘以4。 举个例子&#xff0c;你某门科…

【有营养的算法笔记】从推导证明的角度深剖前缀和与差分算法

&#x1f451;作者主页&#xff1a;进击的安度因 &#x1f3e0;学习社区&#xff1a;进击的安度因&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;有营养的算法笔记 文章目录一、一维前缀和1、算法推导2、代码实现二、二维前缀和1、算法推导2、代码实现三…

数据库范式

1 数据库范式 完全函数依赖 &#xff08;Sno,Cno) —> Grade 是完全函数依赖&#xff0c;学号不能得出成绩&#xff0c;学科号也不能推出成绩。 部分函数依赖 &#xff08;Sno,Cno) --> Sdept 是部分函数依赖&#xff0c;学号能推出院系。 传递依赖 Sno --> Sdep…

肝了十天半月,献上纯手绘“Spring/Cloud/Boot/MVC”全家桶脑图

01 纯手绘Spring思维脑图 纯手绘Spring思维脑图 1.1 基本概念 纯手绘Spring思维脑图-基本概念 事先申明&#xff1a;Spring/Cloud/Boot/MVC的手绘思维脑图以及详细部分解读&#xff0c;这边都是以截图的形式展示出来&#xff0c;如果需要完整的全部原件xmin思维脑图https://gi…

Shell ❀ 三剑客 - Grep + Sed + Awk

文章目录八、三剑客 - Grep Sed Awk1、Grep - 过滤1.1 常用grep参数1.2 使用方法2、Sed - 行匹配2.1 执行原理2.2 常见语法2.3 使用方法2.3.1 地址边界的设定2.3.2 基础编辑命令2.3.3 扩展操作2.3.4 命令执行案例3、Awk - 列匹配3.1 awk能做什么3.2 执行原理3.3 命令的使用3.…

生物素化聚N-异丙基丙烯酰胺,Biotin-PNIPAM

产品名称&#xff1a;生物素化聚N-异丙基丙烯酰胺 英文名称&#xff1a;Biotin-PNIPAM 聚(N-异丙基丙烯酰胺)是一种有机物&#xff0c;化学式为(C6H11NO)n&#xff0c;由单体N-异丙基丙烯酰胺(NIPAM)聚合而成 &#xff0c;主要用于药物控释、生化分离以及化学传感器等。 物…