【数据结构】队列

一 概念和结构

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

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

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

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

出栈 __________________ 入栈

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

相关推荐
一定要AK2 分钟前
萌新联赛第(三)场
数据结构·c++·算法
Cachel wood3 小时前
算法与数据结构:质数、互质判定和裴蜀定理
数据结构·算法·microsoft·机器学习·数据挖掘·langchain
李长渊哦4 小时前
双指针法高效解决「移除元素」问题
数据结构·算法
@我漫长的孤独流浪4 小时前
数据结构测试模拟题(1)
数据结构·c++·算法
王RuaRua6 小时前
[数据结构]6. 队列-Queue
开发语言·数据结构·算法·leetcode
姜行运6 小时前
数据结构【AVL树】
android·数据结构·c#
chenyuhao20249 小时前
链表面试题9之环形链表进阶
数据结构·算法·链表·面试·c#
chenyuhao20249 小时前
链表的面试题8之环形链表
数据结构·算法·链表·面试·c#
晴空闲雲10 小时前
数据结构与算法-线性表-循环链表(Circular Linked List)
数据结构·算法·链表