数据结构--队列(C语言实现)

一、队列

队列是先进先出 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;

这段代码定义了链式队列的两个结构体:

  1. QueueNode是队列的基本节点 ,用于存储数据指向下一节点的指针,负责串联数据形成链表结构
  2. 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;
}
相关推荐
mjhcsp2 小时前
C++信息论超详解析
开发语言·c++
無限進步D2 小时前
Java 基础算法训练
java·开发语言·算法·入门
map1e_zjc2 小时前
Java SpringBoot学习记录(4)
java·开发语言·学习
小毛驴8502 小时前
多线程同步打标记的几种实现方案
java·开发语言·python
Mr_Xuhhh2 小时前
递归之美:合并两个有序链表的优雅解法
java·开发语言
bluebonnet272 小时前
【Python】一些PEP提案(五):注解的延迟求值
开发语言·python
橙露2 小时前
Python 操作 MongoDB:非关系型数据查询与分析
开发语言·python·mongodb
历程里程碑2 小时前
二叉树---二叉树的最大深度
大数据·数据结构·算法·elasticsearch·搜索引擎·全文检索·深度优先
自我意识的多元宇宙2 小时前
树与二叉树--树的基本概念
数据结构·算法