一、队列
队列是先进先出 FIFO的线性数据结构 ,仅允许在队尾插入 、队头删除 ,本文为带头尾指针的链式队列实现
二、队列结点结构和的队列结构的定义
c
typedef int QueueDataType;
// 队列节点结构体
typedef struct QueueNode
{
QueueDataType data; //结点存储的数据
struct QueueNode* next; //指向下一个节点
}QueueNode;
// 队列管理结构体(头尾指针+size)
typedef struct Queue
{
QueueNode* phead; // 队头指针
QueueNode* ptail; // 队尾指针
int size; // 有效元素个数
}Queue;
这段代码定义了链式队列的两个结构体:
QueueNode是队列的基本节点 ,用于存储数据 和指向下一节点的指针,负责串联数据形成链表结构Queue是队列管理结构体,通过队头指针phead、队尾指针ptail快速定位队列首尾以实现高效入队和出队操作,并用size记录当前元素总数,两者配合完成链式队列的完整结构管理
三、Queue.h文件
用于声明队列函数
c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int QueueDataType;
// 队列节点结构体
typedef struct QueueNode
{
QueueDataType data;
struct QueueNode* next;
}QueueNode;
// 队列管理结构体(头尾指针+size)
typedef struct Queue
{
QueueNode* phead; // 队头指针
QueueNode* ptail; // 队尾指针
int size; // 有效元素个数
}Queue;
//初始化队列
void QueueInit(Queue* pq);
//队列判空
bool QueueEmpty(Queue* pq);
//队列有效元素个数
int QueueSize(Queue* pq);
//打印队列
void QueuePrint(Queue* pq);
// ⼊队列,队尾
void QueuePush(Queue* pq, QueueDataType x);
// 出队列,队头
void QueuePop(Queue* pq);
//取队头数据
QueueDataType QueueFront(Queue* pq);
//取队尾数据
QueueDataType QueueBack(Queue* pq);
// 按值查找元素是否存在
QueueNode* QueueFind(Queue* pq, QueueDataType x);
//销毁队列
void QueueDestroy(Queue* pq);
四、Queue.c实现函数
1. QueueInit 函数
QueueInit 函数实现逻辑:
该函数用于对链式队列结构体进行初始化 ,将队头指针 、队尾指针都置为空指针 ,有效元素个数置为 0,为后续队列操作提供安全的初始状态,是使用队列前必须调用的第一个接口
函数功能:初始化队列
c
// 功能:初始化队列
// 参数:pq指向需要初始化的队列结构体
void QueueInit(Queue* pq)
{
// 断言保证传入的队列指针不为空,避免空指针访问
assert(pq);
// 初始化队头指针为空,此时没有任何节点
pq->phead = NULL;
// 初始化队尾指针为空,此时没有任何节点
pq->ptail = NULL;
// 初始状态队列有效元素个数为0
pq->size = 0;
}
2. QueueEmpty 函数
QueueEmpty 函数实现逻辑:
该函数用于判断队列是否为空 ,通过检查队头指针是否为 NULL 来确定队列中是否存在有效节点,是入队、出队、取队头队尾等操作的前置安全判断依据
函数功能:队列判空
c
// 功能:队列判空
// 参数:pq指向队列结构体
// 返回值:队列为空返回true,非空返回false
bool QueueEmpty(Queue* pq)
{
// 断言保证队列指针合法有效
assert(pq);
// 队头指针为NULL表示队列中没有节点,队列为空
return pq->phead == NULL;
}
3. QueueSize 函数
QueueSize 函数实现逻辑:
该函数用于获取队列中当前有效元素的个数 ,直接返回队列结构体中维护的 size 成员变量,空队列返回 0,非空队列返回实际元素数量,为遍历、判断边界提供依据
函数功能:队列有效元素个数
c
// 功能:队列有效元素个数
// 参数:pq指向队列结构体
// 返回值:队列中有效元素的总个数
int QueueSize(Queue* pq)
{
// 断言保证队列指针不为空
assert(pq);
// 如果队列为空,直接返回0
if (QueueEmpty(pq))
{
return 0;
}
// 返回结构体中记录的有效元素个数
return pq->size;
}
4. QueuePrint 函数
QueuePrint 函数实现逻辑:
该函数用于遍历并打印队列中所有有效元素 ,先判断队列是否为空,为空则直接提示,非空则从队头节点开始,依次遍历每个节点并输出数据,直到遍历到队尾,用于调试观察队列内容
函数功能:打印队列
c
// 功能:打印队列
// 参数:pq指向需要打印的队列结构体
void QueuePrint(Queue* pq)
{
// 断言保证队列指针合法
assert(pq);
// 如果队列为空,打印提示信息并直接返回
if (QueueEmpty(pq))
{
printf("The queue is empty.");
return;
}
// 定义临时遍历指针,从队头节点开始遍历
QueueNode* pcur = pq->phead;
// 打印队列前缀提示文字
printf("The queue order is: ");
// 循环遍历整个队列,直到节点指针为NULL(队尾)
while (pcur)
{
// 打印当前节点的数据
printf("%d ", pcur->data);
// 指针后移,访问下一个节点
pcur = pcur->next;
}
// 打印结束换行,格式化输出
printf("\n");
}
5. QueuePush 函数
QueuePush 函数实现逻辑:
该函数用于向队列尾部插入新元素 ,先动态申请新节点内存 ,赋值数据并将 next 置空 ,若队列为空则头尾指针同时指向新节点 ,若非空则将原队尾节点的 next指向新节点并更新队尾指针 ,最后有效元素个数加 1,保证队列先进先出规则
函数功能:⼊队列,队尾
c
// 功能:⼊队列,队尾
// 参数:pq指向队列结构体,x为需要入队的数据
void QueuePush(Queue* pq, QueueDataType x)
{
// 断言保证队列指针不为空
assert(pq);
// 动态分配新节点内存空间
QueueNode* NewNode = (QueueNode*)malloc(sizeof(QueueNode));
// 判断内存分配是否成功
if (NewNode == NULL)
{
// 打印内存分配失败的系统错误信息
perror("malloc failed");
// 分配失败直接退出程序
exit(-1);
}
// 给新节点的数据域赋值
NewNode->data = x;
// 新节点作为尾节点,next指针置空
NewNode->next = NULL;
// 如果队列为空,头尾指针都指向新节点
if (QueueEmpty(pq))
{
pq->phead = NewNode;
pq->ptail = NewNode;
}
// 如果队列不为空,将新节点链接到原队尾后面
else
{
// 原队尾节点的next指向新节点
pq->ptail->next = NewNode;
// 更新队尾指针为新节点
pq->ptail = NewNode;
}
// 队列有效元素个数加1
++pq->size;
}
6. QueuePop 函数
QueuePop 函数实现逻辑:
该函数用于删除队列头部元素 ,先断言队列非空,保存队头节点地址,将队头指针后移到下一个节点,释放原队头节点内存并置空,最后有效元素个数减 1,严格遵循先进先出规则
函数功能:出队列,队头
c
// 功能:出队列,队头
// 参数:pq指向队列结构体
void QueuePop(Queue* pq)
{
// 断言保证队列不为空,空队列不能执行出队操作
assert(!QueueEmpty(pq));
// 保存待删除的队头节点地址
QueueNode* dest = pq->phead;
// 保存队头节点的下一个节点地址
QueueNode* next = pq->phead->next;
// 更新队头指针为下一个节点
pq->phead = next;
// 释放原队头节点的动态内存
free(dest);
// 将原队头指针置空,防止野指针
dest = NULL;
// 队列有效元素个数减1
--pq->size;
}
7. QueueFront 函数
QueueFront 函数实现逻辑:
该函数用于获取队列头部节点的数据,先断言队列非空,直接返回队头指针指向节点的数据域,是获取队列最早入队元素的接口
函数功能:取队头数据
c
// 功能:取队头数据
// 参数:pq指向队列结构体
// 返回值:队头节点存储的数据
QueueDataType QueueFront(Queue* pq)
{
// 断言队列非空,空队列无队头元素
assert(!QueueEmpty(pq));
// 返回队头节点的数据
return pq->phead->data;
}
8. QueueBack 函数
QueueBack 函数实现逻辑:
该函数用于获取队列尾部节点的数据,先断言队列非空,直接返回队尾指针指向节点的数据域,是获取队列最后入队元素的接口
函数功能:取队尾数据
c
// 功能:取队尾数据
// 参数:pq指向队列结构体
// 返回值:队尾节点存储的数据
QueueDataType QueueBack(Queue* pq)
{
// 断言队列非空,空队列无队尾元素
assert(!QueueEmpty(pq));
// 返回队尾节点的数据
return pq->ptail->data;
}
9. QueueFind 函数
QueueFind 函数实现逻辑该函数用于在队列中按值查找节点 ,从队头开始遍历所有节点,逐个比较节点数据与目标值,匹配成功则返回当前节点地址 ,遍历结束未找到则返回 NULL,可用于判断元素是否存在及获取节点地址
函数功能:按值查找元素是否存在
c
// 功能:按值查找元素是否存在
// 参数:pq指向队列结构体,x为需要查找的目标值
// 返回值:找到返回对应节点地址,找不到返回NULL
QueueNode* QueueFind(Queue* pq, QueueDataType x)
{
// 断言保证队列指针合法
assert(pq);
// 定义临时指针,从队头开始遍历
QueueNode* pcur = pq->phead;
// 循环遍历队列所有节点
while (pcur)
{
// 如果当前节点数据等于目标值,找到匹配元素
if (pcur->data == x)
{
// 返回当前节点的地址
return pcur;
}
// 未找到则指针后移,继续查找
pcur = pcur->next;
}
// 遍历完整个队列都未找到,返回NULL
return NULL;
}
10. QueueDestroy 函数
QueueDestroy 函数实现逻辑:
该函数用于销毁整个链式队列,从队头开始遍历所有节点,逐个释放每个节点的动态内存,遍历完成后将队头、队尾指针置空,有效元素个数置为 0,避免内存泄漏和野指针
函数功能:销毁队列
c
// 功能:销毁队列
// 参数:pq指向需要销毁的队列结构体
void QueueDestroy(Queue* pq)
{
// 断言保证队列指针合法
assert(pq);
// 定义临时遍历指针,从队头开始
QueueNode* pcur = pq->phead;
// 循环遍历并释放每一个节点
while (pcur)
{
// 保存当前需要释放的节点地址
QueueNode* dest = pcur;
// 指针后移,保存下一个节点地址
pcur = pcur->next;
// 释放当前节点的内存
free(dest);
// 将释放后的指针置空,防止野指针
dest = NULL;
}
// 队列销毁后,队头指针置空
pq->phead = NULL;
// 队列销毁后,队尾指针置空
pq->ptail = NULL;
// 有效元素个数重置为0
pq->size = 0;
}
五、所有代码
Queue.h
c
// 防止头文件被重复包含
#pragma once
// 包含标准输入输出库,用于打印等操作
#include <stdio.h>
// 包含内存分配库,用于 malloc、free 等操作
#include <stdlib.h>
// 包含布尔类型库,用于 bool、true、false
#include <stdbool.h>
// 包含断言库,用于程序合法性校验
#include <assert.h>
// 定义队列中存储的数据类型为 int
typedef int QueueDataType;
// 队列节点结构体
// 每个节点包含数据和指向下一个节点的指针
typedef struct QueueNode
{
// 节点存储的数据
QueueDataType data;
// 指向下一个节点的指针
struct QueueNode* next;
}QueueNode;
// 队列结构体
// 管理整个队列:队头、队尾、有效元素个数
typedef struct Queue
{
// 指向队列头部节点
QueueNode* phead;
// 指向队列尾部节点
QueueNode* ptail;
// 队列中有效元素的个数
int size;
}Queue;
// 功能:初始化队列
// 参数:pq 指向队列结构体的指针
void QueueInit(Queue* pq);
// 功能:销毁队列,释放所有节点内存
// 参数:pq 指向队列结构体的指针
void QueueDestroy(Queue* pq);
// 功能:元素入队列,插入到队列尾部
// 参数:pq 队列指针,x 入队的数据
void QueuePush(Queue* pq, QueueDataType x);
// 功能:元素出队列,删除队列头部元素
// 参数:pq 队列指针
void QueuePop(Queue* pq);
// 功能:获取队列头部元素的值
// 参数:pq 队列指针
// 返回值:队头元素数据
QueueDataType QueueFront(Queue* pq);
// 功能:获取队列尾部元素的值
// 参数:pq 队列指针
// 返回值:队尾元素数据
QueueDataType QueueBack(Queue* pq);
// 功能:判断队列是否为空
// 参数:pq 队列指针
// 返回值:为空返回 true,不为空返回 false
bool QueueEmpty(Queue* pq);
// 功能:获取队列中有效元素的个数
// 参数:pq 队列指针
// 返回值:队列元素个数
int QueueSize(Queue* pq);
// 功能:打印队列中所有元素
// 参数:pq 队列指针
void QueuePrint(Queue* pq);
// 功能:按值查找队列元素,找到返回节点地址,找不到返回 NULL
// 参数:pq 队列指针,x 要查找的值
// 返回值:找到返回节点地址,找不到返回 NULL
QueueNode* QueueFind(Queue* pq, QueueDataType x);
Queue.c
c
#include "Queue.h"
// 功能:初始化队列
// 参数:pq 队列结构体指针
void QueueInit(Queue* pq)
{
// 校验队列指针不为空,防止空指针访问
assert(pq);
// 队列为空时,队头和队尾指针都置为 NULL
pq->phead = pq->ptail = NULL;
// 队列初始有效元素个数为 0
pq->size = 0;
}
// 功能:判断队列是否为空
// 参数:pq 队列结构体指针
// 返回值:空返回 true,非空返回 false
bool QueueEmpty(Queue* pq)
{
// 校验队列指针有效
assert(pq);
// 队头指针为 NULL 表示队列为空
return pq->phead == NULL;
}
// 功能:打印队列中所有元素
// 参数:pq 队列结构体指针
void QueuePrint(Queue* pq)
{
// 校验队列指针有效
assert(pq);
// 如果队列为空,打印提示并返回
if (QueueEmpty(pq))
{
printf("The queue is empty.");
return;
}
// 定义遍历指针,从队头开始遍历
QueueNode* pcur = pq->phead;
// 打印前缀提示
printf("The queue order is: ");
// 遍历整个队列,直到指针为 NULL
while (pcur)
{
// 打印当前节点数据
printf("%d ", pcur->data);
// 指针后移
pcur = pcur->next;
}
// 打印结束换行
printf("\n");
}
// 功能:元素入队列,插入到队尾
// 参数:pq 队列指针,x 待插入的数据
void QueuePush(Queue* pq, QueueDataType x)
{
// 校验队列指针有效
assert(pq);
// 为新节点申请内存空间
QueueNode* NewNode = (QueueNode*)malloc(sizeof(QueueNode));
// 判断内存申请是否失败
if (NewNode == NULL)
{
// 打印错误信息
perror("malloc failed");
// 异常退出程序
exit(-1);
}
// 给新节点赋值数据
NewNode->data = x;
// 新节点的 next 指针置空
NewNode->next = NULL;
// 如果队列为空,头尾指针都指向新节点
if (QueueEmpty(pq))
{
pq->phead = pq->ptail = NewNode;
}
// 队列不为空,将新节点链接到队尾
else
{
// 原尾节点指向新节点
pq->ptail->next = NewNode;
// 更新队尾指针
pq->ptail = NewNode;
}
// 队列元素个数 +1
++pq->size;
}
// 功能:元素出队列,删除队头元素
// 参数:pq 队列指针
void QueuePop(Queue* pq)
{
// 校验队列不为空,空队列不能出队
assert(!QueueEmpty(pq));
// 保存要删除的队头节点
QueueNode* dest = pq->phead;
// 保存队头的下一个节点
QueueNode* next = pq->phead->next;
// 更新队头指针
pq->phead = next;
// 释放原队头节点内存
free(dest);
// 置空指针,防止野指针
dest = NULL;
// 队列元素个数 -1
--pq->size;
}
// 功能:获取队头元素数据
// 参数:pq 队列指针
// 返回值:队头元素的值
QueueDataType QueueFront(Queue* pq)
{
// 校验队列不为空
assert(!QueueEmpty(pq));
// 返回队头节点存储的数据
return pq->phead->data;
}
// 功能:获取队尾元素数据
// 参数:pq 队列指针
// 返回值:队尾元素的值
QueueDataType QueueBack(Queue* pq)
{
// 校验队列不为空
assert(!QueueEmpty(pq));
// 返回队尾节点存储的数据
return pq->ptail->data;
}
// 功能:获取队列有效元素个数
// 参数:pq 队列指针
// 返回值:元素个数
int QueueSize(Queue* pq)
{
// 校验队列指针有效
assert(pq);
// 队列为空直接返回 0
if (QueueEmpty(pq))
{
return 0;
}
// 返回队列元素个数
return pq->size;
}
// 功能:销毁队列,释放所有节点内存
// 参数:pq 队列指针
void QueueDestroy(Queue* pq)
{
// 校验队列指针有效
assert(pq);
// 定义遍历指针,从队头开始
QueueNode* pcur = pq->phead;
// 遍历所有节点并释放
while (pcur)
{
// 保存当前要释放的节点
QueueNode* dest = pcur;
// 指针后移
pcur = pcur->next;
// 释放节点内存
free(dest);
// 置空指针
dest = NULL;
}
// 头尾指针置空
pq->phead = pq->ptail = NULL;
// 元素个数置 0
pq->size = 0;
}
// 功能:按值查找元素,存在返回节点地址,不存在返回 NULL
// 参数:pq 队列指针,x 查找目标值
// 返回值:找到返回节点地址,找不到返回 NULL
QueueNode* QueueFind(Queue* pq, QueueDataType x)
{
// 校验队列指针有效
assert(pq);
// 定义遍历指针,从队头开始
QueueNode* pcur = pq->phead;
// 遍历队列
while (pcur)
{
// 当前节点数据等于目标值,找到
if (pcur->data == x)
{
// 返回当前节点地址
return pcur;
}
// 未找到,指针后移
pcur = pcur->next;
}
// 遍历结束未找到,返回 NULL
return NULL;
}
test.c
c
// 包含队列头文件
#include "Queue.h"
// 功能:测试队列初始化
// 参数:pq 指向队列结构体的指针
void test_QueueInit(Queue* pq)
{
// 调用队列初始化函数
QueueInit(pq);
}
// 功能:测试数据入队列
// 参数:pq 指向队列结构体的指针
void test_QueuePush(Queue* pq)
{
// 依次向队列中插入数据 1、2、3、4、5、6、7
QueuePush(pq, 1);
QueuePush(pq, 2);
QueuePush(pq, 3);
QueuePush(pq, 4);
QueuePush(pq, 5);
QueuePush(pq, 6);
QueuePush(pq, 7);
// 打印插入后的队列元素
QueuePrint(pq);
}
// 功能:测试数据出队列
// 参数:pq 指向队列结构体的指针
void test_QueuePop(Queue* pq)
{
// 第一次出队(删除队头)
QueuePop(pq);
// 打印出队后的队列
QueuePrint(pq);
// 第二次出队
QueuePop(pq);
// 打印队列
QueuePrint(pq);
// 第三次出队
QueuePop(pq);
// 打印队列
QueuePrint(pq);
}
// 功能:测试获取队头元素
// 参数:pq 指向队列结构体的指针
void test_QueueFront(Queue* pq)
{
// 调用函数获取队头元素
QueueDataType i = QueueFront(pq);
// 打印队头元素
printf("QueueHeadData is %d\n", i);
}
// 功能:测试获取队尾元素
// 参数:pq 指向队列结构体的指针
void test_QueueBack(Queue* pq)
{
// 调用函数获取队尾元素
QueueDataType i = QueueBack(pq);
// 打印队尾元素
printf("QueueTailData is %d\n", i);
}
// 功能:测试获取队列有效元素个数
// 参数:pq 指向队列结构体的指针
void test_QueueSize(Queue* pq)
{
// 调用函数获取元素个数
int size = QueueSize(pq);
// 打印元素个数
printf("QueueSize is %d\n", size);
}
// 功能:测试按值查找队列元素
// 参数:pq 指向队列结构体的指针
void test_QueueFind(Queue* pq)
{
// 查找值为 1 的元素
if (QueueFind(pq, 1))
{
// 找到则打印
printf("Found\n");
}
else
{
// 未找到则打印
printf("Not Found\n");
}
}
// 主函数:程序入口,调用所有测试函数
int main()
{
// 定义队列结构体变量 A
Queue A;
// 测试队列初始化
test_QueueInit(&A);
// 测试数据入队
test_QueuePush(&A);
// 测试数据出队
test_QueuePop(&A);
// 测试获取队头元素
test_QueueFront(&A);
// 测试获取队尾元素
test_QueueBack(&A);
// 测试获取队列元素个数
test_QueueSize(&A);
// 测试按值查找元素
test_QueueFind(&A);
// 销毁队列,释放内存
QueueDestroy(&A);
// 程序正常结束
return 0;
}