【数据结构】队列

一 概念和结构

概念:只允许在一端进行插入操作,在另一端进行删除操作的特殊线性表,队列具有先进先出的特点。

入队列:进行插入操作的一端叫做队尾

出队列:进行删除操作的一端叫做队头

可以把队列想象成购票的队伍,游客想要买票就需要从队尾进入到队列进行排队,想要出队就需要排到队头。

出栈 __________________ 入栈

<------- OOOOOOOOO <---------O


二 队列的实现

如何选型:

数组:

队头\]\[ \]\[ \]\[ 队尾\] 入队:O(1);出队:O(N) \[队尾\]\[ \]\[ \]\[ 队头\] 入队:O(N);出队:O(1) **单链表:** \[队头\]-\>\[\]-\>\[队尾\] 入队O(N);出队O(1) \[队尾\]-\>\[\]-\>\[队头\] 入队O(1);出队O(N) **双链表:** 一个结点会有三个数据,所耗空间太大,所以不考虑

假设队列底层用链表实现,如何将入队操作优化成O(1);

这里就需要用到结构体嵌套的知识点了,假设队列的结构中我们只关注两个结点,一个头结点,一个尾结点,这样入队的操作直接使用尾结点。

cpp 复制代码
struct Queue
{
   结点*head;
   结点*ptail;
}

到这里就可以发现,head和ptail就是指向结点的指针,那么该"结点"就需要单独定义一次,也就是结点的结构

cpp 复制代码
struct QueueNode
{
   int data;
   struct QueueNode*next;//指向下一个结点
}

所以总结下来,就是利用结构嵌套实现队列。

接下来是功能的实现

cpp 复制代码
//初始化
void QueueInit(Queue* pq);

//入队--队尾
void QueuePush(Queue* pq, QDataType x);

//队列判空
bool QueueEmpty(Queue* pq);

//出队--队头
void QueuePop(Queue* pq);

//取队头数据
QDataType QueueFront(Queue* pq);

//取队尾数据
QDataType QueueBack(Queue* pq);

//销毁队列
void QueueDestrory(Queue * pq);

//队列有效元素个数
int QueueSize(Queue* pq);

1.初始化

直接把两个结点置空即可。

cpp 复制代码
//初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
}

2.入队--队尾

先开辟新空间,然后注意判断队列是否为空即可

若为空,直接让head 和 ptail 指向新结点

若不为空,连接队尾即可,记得把ptail重置到新队尾。

cpp 复制代码
//入队--队尾
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		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;
	}
}

3.队列判空

直接判断头结点即可。

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

4.出队--队头

要注意的是,当head==ptail时,也就是只有一个结点的时候,free掉head后,ptail也会被free掉,若不把他置为空,他就会变为野指针。所以此时要phead=patil=NULL

cpp 复制代码
//出队--队头
void QueuePop(Queue* pq)
{
	assert(!QueueEmpty(pq));
	//只有一个节点,phead和ptail都套置为空
	if (pq->phead == pq->ptail)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		QueueNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
}

5.取队头数据

直接取phead的data即可。

cpp 复制代码
//取队头数据
QDataType QueueFront(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->phead->data;
}

6.取队尾数据

直接取ptail的data即可。

cpp 复制代码
//取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->ptail->data;
}

7.销毁队列

与单链表的销毁一样,逐个销毁即可,最后要把phead和ptail都置为空。

cpp 复制代码
//销毁队列
void QueueDestrory(Queue* pq)
{
	assert(pq);
	QueueNode* pcur = pq->phead;
	while (pcur)
	{
		QueueNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->phead = pq->ptail = NULL;
}

8.队列有效元素个数

分为两种方法

第一种遍历链表,时间复杂度为O(1),适合不经常调用的情况使用。

第二种的时间复杂度是O(1),适合经常调用的情况下使用。

cpp 复制代码
//队列有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	//法一:遍历链表--适合不常调用QueueSize
	int size = 0;
	QueueNode* pcur = pq->phead;
	while (pcur)
	{
		size++;
		pcur = pcur->next;
	}
	return size;
	
}
cpp 复制代码
int QueueSize(Queue* pq)
{
	assert(pq);
	//法二:适合常调用QueueSize
	return size;
	
}

这里要注意的是,若使用法二,那么在队列的结构中就需要增加size元素,也就是用空间换时间。那么一些改变队列元素的工具也需要修改:

相关推荐
古译汉书1 小时前
嵌入式铁头山羊STM32-各章节详细笔记-查阅传送门
数据结构·笔记·stm32·单片机·嵌入式硬件·个人开发
橘颂TA2 小时前
【数据结构】解锁数据结构:通往高效编程的密钥
数据结构
LeaderSheepH6 小时前
常见的排序算法
数据结构·算法·排序算法
JasmineX-18 小时前
数据结构——静态链表(c语言笔记)
c语言·数据结构·链表
小卡皮巴拉9 小时前
【笔试强训】Day1
开发语言·数据结构·c++·算法
༾冬瓜大侠༿10 小时前
数据结构:排序
数据结构·算法·排序算法
.YM.Z14 小时前
数据结构——链表
数据结构·链表
hn小菜鸡15 小时前
LeetCode 2570.合并两个二维数组-求和法
数据结构·算法·leetcode
滋滋不吱吱16 小时前
栈的进阶篇
数据结构·算法·leetcode
YQ_ZJH16 小时前
Java List列表创建方法大总结
java·开发语言·数据结构·算法·list