首页 > 其他分享 >队列

队列

时间:2023-04-30 22:32:32浏览次数:25  
标签:head pq 队列 next Queue NULL

1.队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列遵循先进先出FIFO(First In First Out) ,与栈的后进先出相反。

入队列:进行插入操作的一端称为队尾

出队列:进行删除操作的一端称为队头

队列_链表

2.队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低,对于队列的实现,我们使用的是链表。

队列_#include_02

标准库的包含

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

队列结构体的设计


在这里关于结构的选择就不是双选题了,因为无论选左边还是右边作为队尾,数组总是逃不过需要挪动数据的结果。反观链表就显得相对自由。

typedef  int QDatatype;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDatatype data;
}QNode;

结构体QNode表示链表节点,其中包含data域和next指针


但只使用链表结构也不够完善,对于链表来说最痛苦的莫非是尾结点的访问了,但这里要频繁地对尾节点进行访问,不妨再使用一个结构体存储头尾结点,来表示一整个队列。这样处理后,若我们要对头结点进行更改也不需要使用到二级指针了,因为只是对结构体内的数据进行更改因此只需要结构体指针。为了统计大小更加方便也需要一个 size 表示大小。

typedef struct Queue//队列中,多个数据,如头指针尾指针,最好用结构体封装一个函数,不然不好传参
{//单链表给尾指针不能解决问题,尾删还需要前一个tailprev

	QNode* head;
	QNode* tail;
	int size;

}Queue;

结构体Queue包括了两个永远指向队列的队首和队尾的指针

需要实现的接口

//队列的初始化
void QueueInit(Queue* pq);
//队列的销毁
void QueueDestroy(Queue* pq);
//队尾入队列
void QueuePush(Queue* pq,QDatatype x);
//队头出队列
void QueuePop(Queue* pq);
// 获取队列中有效元素个数
int QueueSize(Queue* pq);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq);
// 获取队列头部元素
QDatatype QueueFront(Queue* pq);
// 获取队列队尾元素
QDatatype QueueBack(Queue* pq);

初始化队列

一开始的队列本为空,只需要将头尾结点制空, size 归零。就完成了插入数据前的准备。

void QueueInit(Queue* pq)
{
	assert(pq);

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

判空

为空的判定就相对简单,头尾结点正常情况下是同时为空的,也为了避免特殊情况即二者其中一个为空就可以判断整个队列是空的。

// 检测队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
	//return pq->head = NULL && pq->tail = NULL;
}

入队

传入数值前先断言(养成好习惯),之后像链表那样开辟一个新节点并对其赋值。之后就要面临一个分岔口,队列是否为空,贸然地对头结点解引用就会出现错误,若为空则新节点就变成头结点,非空就链接在尾结点后,这之后自身成为尾结点。

//队尾入队列
void QueuePush(Queue* pq,QDatatype x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	
	newnode->data = x;//传入数据
	newnode->next = NULL;


	if (pq->head == NULL)
	{
		assert(pq->tail == NULL);

		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

	pq->size++;
}

出队

断言后出队需要做到的是:事先将下一结点保存起来,不然当前头结点释放后便无法访问到下一结点了。之后释放头结点,下一结点就变成了头结点,同时 size-- ,保证空间数量的准确避免越界访问。

//队头出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head != NULL);
	//方法一
	//QNode* next = pq->head->next;
	//free(pq->head);
	//pq->head = next;
	//pq->size--;
	////当剩最后一个结点时,tail可能是野指针
	//if (pq->head == NULL)
	//	pq->tail = NULL;

	//方法二
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
	pq->size--;
	
}

获取队列头部/尾部元素

// 获取队列头部元素
QDatatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

// 获取队列尾部元素
QDatatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;

}

获取队列中有效元素个数

这里需要感谢我们在前面在结构内就存储了大小,不然还需要遍历链表才能得到队列的大小。

// 获取队列中有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
	//带头双向循环链表的头结点不可以存放size,因为链表的数据类型是不确定的,如果存放的是char类型的数据,当size加到128链表会溢出
	//这时可以给哨兵位的头结点单独定义一个结构体,但是没必要
	
	////带头双向循环链表的补充
	//int LTSize(LTNode * phead);//需要遍历
	////简化  多个数据定义结构体
	//struct List
	//{
	//	LTNode* phead;
	//	int size;
	//};

}

队列的销毁

同样有 malloc 就有 free ,在这里我们每个结点都是动态开辟出来的,所以都需要释放。就像链表释放那样,一边遍历一边释放。最后再把队列的结构体初始化就完成了。

//队列的销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

源码:

Queue.h 头文件 结构体的定义 函数声明

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef  int QDatatype;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDatatype data;
}QNode;

typedef struct Queue//队列中,多个数据,如头指针尾指针,最好用结构体封装一个函数,不然不好传参
{//单链表给尾指针不能解决问题,尾删还需要前一个tailprev

	QNode* head;
	QNode* tail;
	int size;

}Queue;
//队列的初始化
void QueueInit(Queue* pq);
//队列的销毁
void QueueDestroy(Queue* pq);
//队尾入队列
void QueuePush(Queue* pq,QDatatype x);
//队头出队列
void QueuePop(Queue* pq);
// 获取队列中有效元素个数
int QueueSize(Queue* pq);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq);
// 获取队列头部元素
QDatatype QueueFront(Queue* pq);
// 获取队列队尾元素
QDatatype QueueBack(Queue* pq);


Queue.c

#include"Queue.h"

//队列的初始化
void QueueInit(Queue* pq)
{
	assert(pq);

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

//队列的销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

//队尾入队列
void QueuePush(Queue* pq,QDatatype x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	
	newnode->data = x;//传入数据
	newnode->next = NULL;


	if (pq->head == NULL)
	{
		assert(pq->tail == NULL);

		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

	pq->size++;
}

//队头出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head != NULL);
	//方法一
	//QNode* next = pq->head->next;
	//free(pq->head);
	//pq->head = next;
	//pq->size--;
	////当剩最后一个结点时,tail可能是野指针
	//if (pq->head == NULL)
	//	pq->tail = NULL;

	//方法二
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
	pq->size--;
	
}

// 获取队列中有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
	//带头双向循环链表的头结点不可以存放size,因为链表的数据类型是不确定的,如果存放的是char类型的数据,当size加到128链表会溢出
	//这时可以给哨兵位的头结点单独定义一个结构体,但是没必要
	
	////带头双向循环链表的补充
	//int LTSize(LTNode * phead);//需要遍历
	////简化  多个数据定义结构体
	//struct List
	//{
	//	LTNode* phead;
	//	int size;
	//};

}

// 检测队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
	//return pq->head = NULL && pq->tail = NULL;
}

// 获取队列头部元素
QDatatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

// 获取队列尾部元素
QDatatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;

}


Test.c

#include"Queue.h"


int main()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");

	QueueDestroy(&q);

	return 0;

}

标签:head,pq,队列,next,Queue,NULL
From: https://blog.51cto.com/u_15992140/6238336

相关文章

  • 创建队列时对结构体指针的理解
    1#include<stdio.h>2#include<stdlib.h>34#defineElemTypeint56//定义队列结点7typedefstructQNode8{9ElemTypedata;10structQNode*next;11}QNode,*QNodePrt;1213//定义队首、队尾指针14typedefstruct15{16QNo......
  • 优先队列
    优先队列有两个分支,一个是小根堆,一个是大根堆。这是一个优先队列的定义:priority_queue<int>q;默认是大根堆。大根堆,也就是堆顶是最大的数,按着降序排到堆底。小根堆,也就是堆顶是最小的数,按着升序排到堆底。大根堆定义:priority_queue<int>q;由大根堆转小根堆有三种方......
  • c语言创建队列的链式存储
    #include<stdio.h>#include<stdlib.h>typedefstructLinkNode{intdata;structLinkNode*next;}LinkNode;typedefstructLink{LinkNode*front,*rear;//frontrear为链表的伴随指针}LinkQueue;//初始化voidInitQueue(LinkQueue*......
  • 第三章-栈 队列和数组
    栈stack数据接口三要素逻辑,运算,存储只允许在一端进行数据插入和删除操作.LIFO规则,lastinfirstout先进后出联想到烤串.doge卡特兰数(catalan),n个不同元素进栈,出栈元素不同排列的个数为顺序栈链栈只在头结点插入和删除就是链栈队列FIFOfirstinfirsto......
  • RabbitMQ 实现消息队列延迟
    1.概述要实现RabbitMQ的消息队列延迟功能,一般采用官方提供的rabbitmq_delayed_message_exchange插件。但RabbitMQ版本必须是3.5.8以上才支持该插件,否则得用其死信队列功能。2.安装RabbitMQ延迟插件检查插件使用rabbitmq-pluginslist命令用于查看RabbitMQ安装的插件。rabbitmq-pl......
  • SpringBoot RabbitMQ死信队列
    1.死信定义无法被消费的消息,称为死信。如果死信一直留在队列中,会导致一直被消费,却从不消费成功,专门有一个存放死信的队列,称为死信队列(DDX,dead-letter-exchange)。死信队列DLX,DeadLetterExchange的缩写,又死信邮箱、死信交换机。其实DLX就是一个普通的交换机,和一般的交换机没有......
  • RabbitMQ 实现消息队列延迟
    1.概述要实现RabbitMQ的消息队列延迟功能,一般采用官方提供的rabbitmq_delayed_message_exchange插件。但RabbitMQ版本必须是3.5.8以上才支持该插件,否则得用其死信队列功能。2.安装RabbitMQ延迟插件检查插件使用rabbitmq-pluginslist命令用于查看RabbitMQ安装的插件。rabb......
  • SpringBoot RabbitMQ死信队列
    1.死信定义无法被消费的消息,称为死信。如果死信一直留在队列中,会导致一直被消费,却从不消费成功,专门有一个存放死信的队列,称为死信队列(DDX,dead-letter-exchange)。死信队列DLX,DeadLetterExchange的缩写,又死信邮箱、死信交换机。其实DLX就是一个普通的交换机,和一般的交换......
  • Provisional heads are shown、NullPointerException空指针异常?堆栈与队列的区别?Java
    Provisionalheadsareshown排查是否插件拦截,我的以前没有这种,所以排除本地网络节点问题,连接不到图片服务器,以下是解决方法:1.进入到C盘Windows文件夹System32/drivers/etc目录下,打开hosts文件,绑定下2.改下本地dns为公共dns网络节点导致的问题,一般为运营商导致,产生问题的原因为......
  • C++实现一个简单的生产者-消费者队列
    本文的代码都是ChatGPT生成,我只是做了微小的调整和整合,AI提示词如下:设计一个C++类,支持生产者-消费者模型,可以通过size函数获取剩余数量可能第一次生成的不一定合适,多刷新几次。生成的ProducerConsumerQueue.h代码如下:#ifndefPRODUCER_CONSUMER_QUEUE_H#definePRODUCER_CON......