【数据结构】队列

一 概念和结构

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

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

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

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

出栈 __________________ 入栈

<------- 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元素,也就是用空间换时间。那么一些改变队列元素的工具也需要修改:

相关推荐
闪电悠米3 小时前
力扣hot100-41.缺失的第一个正数-原地哈希详解
数据结构·算法·哈希算法
WL学习笔记4 小时前
堆(数据结构)
数据结构·
wabs6668 小时前
关于图论【卡码网100.最大岛屿的面积的思考】
数据结构·算法·图论
yuannl109 小时前
图的最短路径Dijkstra
数据结构
zander25810 小时前
138. 随机链表的复制
数据结构·算法·链表
过期动态10 小时前
【LeetCode 热题 100】找到字符串中所有字母异位词
java·数据结构·算法·leetcode·职场和发展·rabbitmq
来一碗刘肉面11 小时前
栈在递归中的应用
数据结构·算法
皓月斯语11 小时前
程序设计语言的特点
开发语言·数据结构·c++
邪修king11 小时前
C++ 进阶终章:异常机制与智能指针全解 —— 从错误处理到 RAII 资源管理,打通现代 C++ 的核心命脉
android·数据结构·c++
Adios79419 小时前
设置交集大小至少为2
数据结构·算法·leetcode