数据结构--队列(图文)

发布于:2024-07-01 ⋅ 阅读:(12) ⋅ 点赞:(0)

队列是一种特殊的线性表,其核心特点是先进先出。

概念及特点:

概念

队列是一种只允许在一端进行插入数据操作,在另一端进行删除数据操作的线性表。进行插入操作的一端被称为队尾(Tail 或 Rear),进行删除操作的一端被称为队头(Head 或 Front)。在日常生活中,我们可以将队列比作排队等候的场景,排在前面的人会先得到服务,新来的人则排在队尾等待。
在这里插入图片描述

特点:

  1. 先进先出(FIFO):队列的基本操作是入队和出队。入队操作是在队尾插入一个元素,而出队操作是从队头删除一个元素。这意味着最先进入队列的元素将最先被移除。

  2. 操作受限:与普通的线性表不同,队列的操作是受限制的。只能在队尾添加元素,只能在队头删除元素。

  3. 两种实现方式:队列可以通过数组或链表来实现。使用数组实现时,可能会遇到数组空间受限的问题,但可以通过循环队列的方式解决。使用链表实现时,可以动态地分配内存,但可能会有额外的内存开销。

  4. 动态变化:队列是一种动态的数据结构,其大小可以根据需要动态变化。在入队和出队操作时,队列的大小会相应地增加或减少。

  5. 应用广泛:队列在计算机科学中有广泛的应用,如广度优先搜索、任务调度、缓冲处理、线程同步等。

队列的操作

  • 初始化:创建一个空队列。

  • 入队:将一个元素添加到队列的末尾,即队尾。

  • 出队:从队列的开头,即队首,移除元素并返回该元素的值。

  • 返回队首元素:返回队列中的第一个元素的值,但不从队列中删除该元素。

  • 返回队尾元素):返回队列中的末尾元素的值,但不从队列中删除该元素。

  • 获取队列元素个数:返回队列中的末尾元素的值,但不从队列中删除该元素。

  • 判断队列是否为空: 检查队列中是否没有任何元素。

  • 销毁队列:释放队列所占用的存储空间,使得队列不再存在。

队列的应用

队列的应用场景包括但不限于:

  • 打印机任务队列:打印机按照任务进入队列的顺序进行打印,确保了公平性和先到先服务的原则。

  • 网络请求处理:服务器使用队列来管理并响应客户端的请求,保证了请求按照到达顺序被处理。

  • 生产线作业:在生产线上,队列模型用于管理产品的生产流程,确保每个产品都能按照顺序经过各个加工阶段。

  • 消息队列:在消息传递系统中,消息被放入队列,然后由消费者按照顺序处理,支持异步消息处理和负载均衡。

  • 任务队列:在Web服务器和后台处理系统中,任务队列用于管理和调度异步任务,如发送电子邮件、处理图像等。

  • 优先级队列:在需要优先处理的场景中,如紧急任务或高价值任务,使用优先级队列可以确保这些任务能够优先被执行。

队列的实现

这里我们使用链表来实现队列。

首先创建三个文件:

  • Queue.h —— 用于声明函数的头文件。
  • Queue.c —— 队列主要函数的实现。
  • test.c——测试队列。

队列节点的定义

这里和单链表结构一致。

// 链式结构:表示队列 
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

队列指针的定义

将队列的首尾指针封装成一个结构体,可以方便函数调用,统一接口。再使用一个整型size记录元素个数,利于其他函数功能实现。

// 队列的结构 
typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;
}Queue;

在这里插入图片描述

初始化

// 初始化队列 
void QueueInit(Queue* q)
{
	assert(q);//队列必须存在
	q->head = q->tail = NULL;
	q->size = 0;
}

销毁队列

创建指针循环遍历链表,每次记录下指针的下一个节点,释放指针指向的节点,指针指向下一个节点

// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q);
	while (q->head)//释放所有节点
	{
		QNode* next = q->head->next;
		free(q->head);
		q->head = next;
	}
	q->head = q->tail = NULL;
	q->size = 0;
}

入队列(队尾插入)

  • 情况一:队列为空直接申请新节点给头节点和尾节点;
  • 情况二:队列不为空,将新节点接入尾节点后即可。
// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)//判定是否申请成功
	{
		perror("newnode error\n");
		exit(1);
	}
	newnode->data = data;
	newnode->next = NULL;
 
	if (q->head == NULL)//对空队列入队的处理
	{
		q->head = q->tail = newnode;
	}
	else               //对非空队列入队的处理
	{
		q->tail->next = newnode;
		q->tail = newnode;
	}
	q->size++;
}

出队列(队头删除)

  • 情况一:队列只有一个节点,直接释放即可;
  • 情况二:队列有多个节点,将头节点的下一个节点保存后释放头节点,在让头节点指向保存的节点。
// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q);
	assert(q->head);//队列不能为空
	if (q->head == q->tail)//对只有一个元素的队列的出队处理
	{
		free(q->head);
		q->head = q->tail = NULL;
	}
	else          //对存在多个元素的队列的出队处理
	{
		QNode* next = q->head->next;
		free(q->head);
		q->head = next;
	}
	q->size--;
}

获取队头元素

// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(q->head);//队列不能为空
	return q->head->data;
}

获取队尾元素

// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(q->head);
	return q->tail->data;
}

获取队列元素个数

// 获取队列中有效元素个数 
int QueueSize(Queue* q)
{
	assert(q);
	return q->size;
}

队列判空

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->size == 0;
}

队列源码

Queue.h

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
 
// 链式结构:表示队列 
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;
 
// 队列的结构 
typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;
}Queue;
 
// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

Queue.c

#include"Queue.h"
 
// 初始化队列 
void QueueInit(Queue* q)
{
	assert(q);//队列必须存在
	q->head = q->tail = NULL;
	q->size = 0;
}
 
// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)//判定是否申请成功
	{
		perror("newnode error\n");
		exit(1);
	}
	newnode->data = data;
	newnode->next = NULL;
 
	if (q->head == NULL)//对空队列入队的处理
	{
		q->head = q->tail = newnode;
	}
	else               //对非空队列入队的处理
	{
		q->tail->next = newnode;
		q->tail = newnode;
	}
	q->size++;
}
 
// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q);
	assert(q->head);//队列不能为空
	if (q->head == q->tail)//对只有一个元素的队列的出队处理
	{
		free(q->head);
		q->head = q->tail = NULL;
	}
	else          //对存在多个元素的队列的出队处理
	{
		QNode* next = q->head->next;
		free(q->head);
		q->head = next;
	}
	q->size--;
}
 
// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(q->head);//队列不能为空
	return q->head->data;
}
 
// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(q->head);
	return q->tail->data;
}
 
// 获取队列中有效元素个数 
int QueueSize(Queue* q)
{
	assert(q);
	return q->size;
}
 
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->size == 0;
}
 
// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q);
	while (q->head)//释放所有节点
	{
		QNode* next = q->head->next;
		free(q->head);
		q->head = next;
	}
	q->head = q->tail = NULL;
	q->size = 0;
}