数据结构初阶:队列

本篇博客主要讲解队列的相关知识。

目录

1.队列

[1.1 概念与结构](#1.1 概念与结构)

[1.2 队列头文件(Queue.h)](#1.2 队列头文件(Queue.h))

[1.2.1 定义队列结点结构](#1.2.1 定义队列结点结构)

[1.2.2 定义队列的结构](#1.2.2 定义队列的结构)

[1.3 队列源代码(Queue.h)](#1.3 队列源代码(Queue.h))

[1.3.1 队列的初始化](#1.3.1 队列的初始化)

[1.3.2 队列的销毁](#1.3.2 队列的销毁)

[1.3.3 入队---队尾](#1.3.3 入队---队尾)

[1.3.4 判空](#1.3.4 判空)

[1.3.5 出队--队头](#1.3.5 出队--队头)

[1.3.6 取队头数据 和 取队尾数据](#1.3.6 取队头数据 和 取队尾数据)


1.队列

1.1 概念与结构

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

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

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

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

1.2 队列头文件(Queue.h)
复制代码
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

//定义结点的结构
typedef int QDataTpe;
typedef struct QueueNode
{
	QDataTpe data;
	struct QueueNode* next;
}QueueNode;

//定义队列的结构
typedef struct Queue {
	QueueNode* phead;//队头
	QueueNode* ptail;//队尾
	int size;//记录有效数据个数
}Queue;

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

//入队---队尾
void QueuePush(Queue* pq, QDataTpe x);
//出队---队头
void QueuePop(Queue* pq);

//取队头数据
QDataTpe QueueFront(Queue* pq);
//取队尾数据
QDataTpe QueueBack(Queue* pq);

bool QueueEmpty(Queue* pq);
//队列有效元素个数
int QueueSize(Queue* pq);
1.2.1 定义队列结点结构
复制代码
//定义结点的结构
typedef int QDataTpe;
typedef struct QueueNode
{
	QDataTpe data;
	struct QueueNode* next;
}QueueNode;

代码逻辑:

首先,通过typedef定义了一个整数类型QDataTpe ,这里实际上是将int类型取了一个别名QDataTpe

然后,定义了一个结构体QueueNode,这个结构体包含了两个成员:

  • **QDataTpe data:**用于存储节点的数据。
  • struct QueueNode* next 一个指向结构体QueueNode的指针,用于指向下一个节点,从而形成链表结构。

通过这样的定义,可以方便地创建和操作队列节点,为构建队列数据结构奠定了基础。

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

代码逻辑:

定义了一个结构体Queue,其中包含了三个成员:

  • QueueNode* phead 一个指向QueueNode结构体的指针,用于表示队列的队头。
  • QueueNode* ptail 一个指向QueueNode结构体的指针,用于表示队列的队尾。
  • int size:用于记录队列中有效数据的个数。

通过这样的定义,可以方便地对队列进行操作,如入队、出队等。

1.3 队列源代码(Queue.h)

复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
}
//销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QueueNode* pcur = pq->phead;
	while (pcur)
	{
		QueueNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->phead = pq->ptail = NULL;
}
//入队---队尾
void QueuePush(Queue* pq, QDataTpe x)
{
	assert(pq);
	//newnode
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	//队列为空,newnode是队头也是队尾
	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else {
		//队列非空,直接插入到队尾
		pq->ptail->next = newnode;
		pq->ptail = pq->ptail->next;
	}
	pq->size++;
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->phead == 0;
}
//出队---队头
void QueuePop(Queue* pq)
{
	assert(!QueueEmpty(pq));
	//只有一个结点的情况
	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;
	}
	pq->size--;
}
//取队头数据
QDataTpe QueueFront(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->phead->data;
}
//取队尾数据
QDataTpe QueueBack(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->ptail->data;
}

//队列有效元素个数
int QueueSize(Queue* pq)
{
	return pq->size;
}
1.3.1 队列的初始化
复制代码
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
}

代码逻辑:

函数的参数是一个指向Queue结构体的指针pq。在函数内部,首先使用assert判断pq是否有效,首先使用assert函数检查pq是否有效。

然后,将队列的队头指针 pq -> phead 和 队尾指针 pq ->ptail 都初始化为NULL,表示初始状态下队列为空。

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

代码逻辑:

函数的参数是一个指向Queue结构体的指针pq 首先使用assert函数检查pq是否有效。

然后,通过一个循环遍历队列的节点。 在循环中,先保存当前节点的下一个节点的指针next然后释放当前节点的内存空间 ,最后将当前节点指针移动到下一个节点。当循环结束后,将队列的队头指针pq->phead和队尾指针pq->ptail都设置为NULL,表示队列已被销毁。

1.3.3 入队---队尾
复制代码
//入队---队尾
void QueuePush(Queue* pq, QDataTpe x)
{
	assert(pq);
	//newnode
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	//队列为空,newnode是队头也是队尾
	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else {
		//队列非空,直接插入到队尾
		pq->ptail->next = newnode;
		pq->ptail = pq->ptail->next;
	}
	pq->size++;
}

代码逻辑:

函数的参数是一个指向Queue结构体的指针pq和要入队的元素x 。首先使用assert函数检查pq是否有效。然后,使用malloc函数分配一个新的QueueNode节点newnode,并为其分配内存空间。如果内存分配失败(newnodeNULL),则输出错误信息并退出程序。

接下来,将入队元素的值赋给新节点的data成员, 并将新节点的next指针设置为NULL

**然后,通过判断队列是否为空来确定新节点的插入位置。**如果队列为空,将新节点同时设置为队头和队尾;如果队列非空,将新节点插入到队尾,并更新队尾指针。

最后,将队列的有效数据个数加1

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

代码逻辑同上篇博客类似,不过多赘述。

1.3.5 出队--队头
复制代码
//出队---队头
void QueuePop(Queue* pq)
{
	assert(!QueueEmpty(pq));
	//只有一个结点的情况
	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;
	}
	pq->size--;
}

代码逻辑:

函数的参数是一个指向Queue结构体的指针pq 首先使用assert函数和QueueEmpty函数来检查队列是否为空,如果队列为空则会产生断言错误。

接下来,通过判断队列中节点的数量来确定出队的具体操作。 如果队列中只有一个节点,那么释放该节点的内存空间,并将队头和队尾指针都设置为NULL。如果队列中有多个节点,那么保存队头节点的下一个节点的指针next,释放队头节点的内存空间,然后将队头指针更新为next

最后,将队列的有效数据个数减1

1.3.6 取队头数据 和 取队尾数据
复制代码
//取队头数据
QDataTpe QueueFront(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->phead->data;
}
//取队尾数据
QDataTpe QueueBack(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->ptail->data;
}

代码逻辑:

  • QDataTpe QueueFront(Queue* pq) 这个函数用于获取队列的队头数据。函数首先使用assert函数和QueueEmpty函数来检查队列是否为空,如果队列为空则会产生断言错误。如果队列不为空,那么直接返回队头节点的data成员的值。
  • QDataTpe QueueBack(Queue* pq) 这个函数用于获取队列的队尾数据。函数的检查流程和QueueFront函数类似,也是先检查队列是否为空, 如果不为空,那么直接返回队尾节点的data成员的值。

2. 小结

以上便是本篇博客关于队列的所有内容,如果能给大家带来知识,还请支持支持博主。

相关推荐
noravinsc37 分钟前
python提升图片清晰度
开发语言·python
假女吖☌44 分钟前
Maven 编译指定模版
java·开发语言·maven
Y.O.U..2 小时前
力扣HOT100——无重复字符的最长子字符串
数据结构·c++·算法·leetcode
水w4 小时前
【Python爬虫】简单案例介绍1
开发语言·爬虫·python
qq_365911606 小时前
GPT-4、Grok 3与Gemini 2.0 Pro:三大AI模型的语气、风格与能力深度对比
开发语言
虔城散人7 小时前
C语言 |位域结构体
c语言
Hanson Huang7 小时前
【数据结构】堆排序详细图解
java·数据结构·排序算法·堆排序
慕容静漪7 小时前
如何本地安装Python Flask并结合内网穿透实现远程开发
开发语言·后端·golang
ErizJ7 小时前
Golang|锁相关
开发语言·后端·golang