数据结构 --- 队列

队列 --- Queue

前言

队列是一种特殊的线性表,其特性是先进先出,只能在队尾入数据,在队头出数据,本次实现使用链表结构实现队列,对比数组实现有下列优点:

尽管现在来看两者的时间复杂度是差不多的,但是对于链表来说,我们只需要再定义一个尾节点来表示队尾即可将入队操作的时间复杂度优化为O(1),而数组的出队操作是无法优化时间复杂度的,所以综上所述本次实现选择链表结构实现队列。故而逻辑结构上是线性的,物理结构上不一定是线性的。
本次实现使用C语言实现。

一、整体结构

本次实现队列底层结构是链表,所以说有一个节点结构类型,并且队列操作数据只有队头和队尾位置可以操作,所以将其抽离出来,这就是队列的结构。

节点的结构:

c 复制代码
typedef int QDataType;
// 队列的节点结构
typedef struct QueueNode
{
	QDataType data;          // 数据域
	struct QueueNode* next;  // 指针域
}QueueNode;

队列的结构:

c 复制代码
// 队列的结构
typedef struct Queue
{
	QueueNode* phead;     // 队头
	QueueNode* ptail;     // 队尾
	// int size;          // 有效数据个数
}Queue;

二、相关方法

1.初始化

初始化即将队列phead和ptail置为空即可。

c 复制代码
// 初始化
void QueueInit(Queue* pq)
{
	// pq不能为空
	assert(pq);

	// 初始化成空
	pq->phead = pq->ptail = NULL;
	// pq->size = 0;
}

创建一个空队列q1并初始化它,

监视窗口演示:

2.销毁

常见的标记销毁位置起始的节点,并记录此位置的下一个节点,之后循环销毁节点,完成后将phead和ptail置为NULL。

c 复制代码
// 销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);

	// 标记队头
	QueueNode* pcur = pq->phead;

	// 销毁节点
	while (pcur)
	{
		// 记录pcur的下一个节点
		QueueNode* pnext = pcur->next;
		free(pcur);
		pcur = pnext;
	}

	// 销毁完成后phead和ptail置为NULL
	pq->phead = pq->ptail = NULL;
	// pq->size =0;
}

3.入队 - - - 队尾插插入数据

入队两步骤:

1)申请新节点,动态开辟(malloc)一个节点大小的内存空间,更新数据域为入队元素x,再将指针域置为NULL。

2)判断队列是否有元素,若有元素则直接在队尾ptail后面插入数据,并更新队尾指针指向;若没有元素,则新插入的节点newnode既是队头phead也是队尾ptail。

c 复制代码
// 入队 --- 队尾入
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	// 申请新节点
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	// 当队列为空时
	if (pq->phead == NULL)
	{
		// 新节点就是队头和队尾
		pq->phead = pq->ptail = newnode;
	}
	// 当队列不为空时
	else
	{
		// 队尾入队列
		pq->ptail->next = newnode;
		// 移动队尾指针
		pq->ptail = pq->ptail->next;
	}
	// pq->size++;
}

向初始化完成后的队列q1入队三个数据:10,20,30

监视窗口演示:

4.出队 - - - 队头删除数据

出队两步骤:

1)判断队列是否是非空状态,只有有元素才可以出队。

2)判断队列的节点个数,当只有一个节点时,销毁此节点并将phead和ptail置为NULL;当有多个节点时,也是销毁此节点,并更新队头指针。

c 复制代码
// 出队 --- 队头出
void QueuePop(Queue* pq)
{
	// 队列非空才可以出队
	assert(!QueueEmpty(pq));

	// 当队列只有一个节点
	if (pq->phead == pq->ptail)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	// 当队列有多个节点时
	else
	{
		// 标记队头的下一个节点
		QueueNode* pnext = pq->phead->next;
		free(pq->phead);
		// 移动队头至下一个节点
		pq->phead = pnext;
	}
	//pq->size--;
}

向入队完成后的队列q1出队前两个数据:10,20

监视窗口演示:

5.获取队头 / 队尾元素

获取两者的元素各自访问队头队尾指针的数据域即可,前提是队列非空。

获取队头元素:

c 复制代码
// 获取队头元素
QDataType QueueFront(Queue* pq)
{
	// 队列非空才可以获取
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

获取队尾元素:

c 复制代码
// 获取队尾元素
QDataType QueueBack(Queue* pq)
{
	// 队列非空才可以获取

	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}

6.判空

队列为空的条件是队头指针或者队尾指针等于NULL。

c 复制代码
// 判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->phead == NULL;
}

7.获取队列有效元素个数

有两种方法:

1)遍历队列,定义一个计数器size,每访问一个节点计数器加一。

2)在队列属性里多增加一个size属性,直接返回size即可,增加这一个属性之后,初始化,入队,出队,销毁这种需要改变size大小的方法也需要对size进行操作。

c 复制代码
// 获取队列有效元素个数
int Queuesize(Queue* pq)
{
	// 方法一:遍历,统计
	// 适用于非频繁访问有效数据个数的场景 : O(N)
	 
	// 标记队头
	QueueNode* pcur = pq->phead;
	int size = 0;
	while (pcur)
	{
		size++;
		pcur = pcur->next;
	}
	return size;

	// 方法二:给队列增加一个size属性
	// 适用于频繁访问有效数据个数的场景 :O(1)
	//return pq->size;
}
相关推荐
学不动CV了3 分钟前
ARM单片机中断及中断优先级管理详解
c语言·arm开发·stm32·单片机·嵌入式硬件·51单片机
༾冬瓜大侠༿1 小时前
数据结构:排序
数据结构·算法·排序算法
番茄大杀手1 小时前
C/C++柔性数组
c语言·柔性数组
冉佳驹2 小时前
C语言 ——— 操作符
c语言·操作符·隐式类型转换·原、反、补码·左移右移操作符·结构成员访问操作符·按位操作符
l1t2 小时前
测试duckdb的C插件模板的编译加工和加载
c语言·开发语言·数据库·插件·duckdb
.YM.Z4 小时前
数据结构——链表
数据结构·链表
hhhwx6665 小时前
Linux学习记录--利用信号量来调度共享资源(2)
linux·c语言·c++·学习
yihai-lin5 小时前
Rust/C/C++ 混合构建 - Cmake集成Cargo编译动态库
c语言·c++·rust
10001hours5 小时前
C语言第20讲
c语言·开发语言
hn小菜鸡6 小时前
LeetCode 2570.合并两个二维数组-求和法
数据结构·算法·leetcode