数据结构之队列

Hello,各位小伙伴们上期我们学习了栈这样的数据结构,今天让我们一起学习一下它的孪生兄弟队列。

队列的基本概念和结构

概念:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) ​

入队列:进行插入操作的一端称为队尾 ​

出队列:进行删除操作的一端称为队头

队列底层结构选型

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

队列的实现

队列的基本结构:

cpp 复制代码
typedef int QNDatatype;
typedef struct QueueNode
{
	QNDatatype data;
	struct QueueNode* next;
}QueueNode;//这里实在定义队列的节点
//同时要定义队列的节点指针----队列的结构
typedef struct Queue
{
	QueueNode* phead;//队头
	QueueNode* ptail;//队尾
}

队列的结点结构与链表类似,但队列要满足基本从一端入队列,从另一端出队列如果我们像链表那样在头节点进行结点的删除,尾节点进行节点的增加,我们需要遍历链表来找到尾结点这样时间复杂度就变为O(n),所以我们额外定义了一个结构体变量在链表的两端固定头结点和尾结点。

实现队列我们要实现一下基本功能:

cpp 复制代码
//队列的初始化
void QueueInit(Queue* ps);
//判断队列是否为空
bool QueueEmpty(Queue* ps);
//判断队列有效数据个数
int QueueNumSize(Queue* ps);
//入队列
void QueuePush(Queue* ps, QNDatatype x);
//删除队列节点
void QueuePop(Queue* ps);
//取对头数据
QNDatatype QueueFront(Queue* ps);
//取队尾数据
QNDatatype QueueBack(Queue* ps);
//队列的销毁
void QueueDestory(Queue* ps);

1.队列的初始化

cpp 复制代码
oid QueueInit(Queue* ps)
{
	assert(ps);
	ps->phead = ps->ptail = NULL;
	ps->size = 0;
}

2.判断队列是否为空

cpp 复制代码
bool QueueEmpty(Queue* ps)
{
	assert(ps);
	return ps->phead == NULL;//如果为空bool则会显示true
}

3.判断队列中有效数据的个数

cpp 复制代码
int QueueNumSize(Queue* ps)
{
	int size = 0;
	QueueNode* pur = ps->phead;
	while (pur)
	{
		size++;
		pur = pur->next;
	}
	return size;
}

在这里我们可以了解到时间复杂度为O(n),为了降低 时间复杂度我们可以在队列的结构体中多设置一个参数,int size 在入队列和出队列的时候我们只需要对ps->size加加或者减减就可以了,这样我们在此函数中直接返回ps->size即可。这样时间复杂度就会降低为O(1)。

4.入队列

在入队列操作时我们要注意在进行新的内存空间申请时注意变量类型,因为队列结点结构,我们在队列结点结构上进行了重定义,这里一定要注意一下万万不可写成队列结构。

cpp 复制代码
void QueuePush(Queue* ps, QNDatatype x)
{
	assert(ps);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;//将申请好的节点空间赋值并间下一个节点指针赋值为NULL
	//队列为空的情况下
	if (ps->phead == NULL)
	{
		ps->phead = ps->ptail = newnode;
	}
	else {
	//队列不为空的情况下
		ps->ptail->next = newnode;//将newnode插入到尾节点的下一个节点
		ps->ptail = ps->ptail->next;//同时将尾节点的下一个节点置为尾节点
	}
}

5.删除队列结点

删除队列结点我们要注意结点个数,通过结点个数来分情况。但结点个数为1的时候队列的phead与ptail在同一个位置,直接free掉即可。

但结点个数有多个时,我们要在头结点的位置开始删除,但要创建一个新的变量next来记录头结点的下一个结点(否则如果直接删除了头结点就早不到下一个结点了)删除该结点后将next赋值给该队列的头结点。

cpp 复制代码
void QueuePop(Queue* ps)
{
	assert(ps);
	assert(!QueueEmpty(&ps));
	//只有一个节点
	if (ps->phead->next == NULL)
	{
		free(ps->phead);
		ps->phead = ps->ptail = NULL;
	}//多个节点的情况
	else {
		Queue* next = ps->phead->next;
		free(ps->phead);
		ps->phead = next;
	}
	
}

6.取对头数据

取对头数据我们要进行两次断言,一次为传递过来的队列的指针不能为空,第二个为传递过来的队列里面的结点不能为空!然后直接返回头结点的数据即可。

cpp 复制代码
QNDatatype QueueFront(Queue* ps)
{
	assert(ps);
	assert(!QueueEmpty(&ps));
	return ps->phead->data;
}

7.取队尾数据

cpp 复制代码
QNDatatype QueueBack(Queue* ps)
{
	assert(ps);
	assert(!QueueEmpty(&ps));
	return ps->ptail->data;
}

8.队列的销毁

队列的销毁与链表的销毁类似,我们要循环销毁,创建一个新的队列结点结构体变量来记录队列的头结点,通过判断该结点是否为NULL来循环销毁结点。最后要记得将队列的两个指针释放掉。

cpp 复制代码
void QueueDestory(Queue* ps)
{
	assert(ps);
	QueueNode* pur = ps->phead;
	while (pur)
	{
		QueueNode* next = pur->next;
		free(pur);
		pur = next;
	}
	ps->phead = ps->ptail = NULL;
}
相关推荐
唐诺2 小时前
几种广泛使用的 C++ 编译器
c++·编译器
XH华2 小时前
初识C语言之二维数组(下)
c语言·算法
冷眼看人间恩怨3 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡3 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客3 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin3 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
axxy20004 小时前
leetcode之hot100---240搜索二维矩阵II(C++)
数据结构·算法
yuanbenshidiaos5 小时前
c++---------数据类型
java·jvm·c++
十年一梦实验室5 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
taoyong0015 小时前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法