1.链队列的简介
1.1链队列的结构
队列是一种我们比较常见的数据结构,它只允许在一端进行插入数据操作,在另一端进行删除数据的特殊线性表。队列可以用数组和链表的结构实现,使用单链表的结构更优一些,因为如果使用数组的结构,出队列在数组头出数据,效率会比较低。
1.2链队列的特征
链队列的底层实现逻辑便是对单链表的访问,只不过需要头尾两个指针来提高储存和访问数据的效率,其次队列最有特征的数据储存访问方式便是先进先出FIFO(First In First Out),这可以与栈的先进后出做对比,入队列则在队尾进行插入操作,出队列则在对头进行删除操作。
2.链队列的实现
上面说到链队列是用单列表实现的,所以用个结构体来定义节点,这里是否带头可根据自己喜好选择,这里为不带头单列表,数据类型最好重命名为了以后可以适应不同的数据类型,提高代码可读性。另需一个结构体来储放队列的头尾指针,再用一个整形来记录队列药效数据个数,这个结构体的作用是为了封装两个指针,之后向函数传结构体的地址即可,避免了二级指针的使用,如果不用这个结构体封装,则必须向函数传两个指针的地址,则需要二级指针,增加了理解难度,和运行效率。
2.1Queue.h
头文件
#pragma once
#include<stdlib.h>
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
定义节点和头尾指针
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
int val;
}QNode;
typedef struct Queue
{
// 队列的头指针
QNode* phead;
// 队列的尾指针
QNode* ptail;
// 队列的大小
int size;
}Queue;
函数接口
// 初始化队列
void QueueInit(Queue* pq);
// 销毁队列
void QueueDestroy(Queue* pq);
// 向队列中添加元素
void QueuePush(Queue* pq, QDataType x);
// 从队列中删除元素
void QueuePop(Queue* pq);
// 获取队列头部元素
QDataType QueueFront(Queue* pq);
// 获取队列尾部元素
QDataType QueueBack(Queue* pq);
// 获取队列大小
int QueueSize(Queue* pq);
// 判断队列是否为空
bool QueueEmpty(Queue* pq);
2.2Queue.c
初始化队列
// 初始化队列
void QueueInit(Queue* pq)
{
// 断言pq不为空
assert(pq);
// 初始化头指针和尾指针为NULL
pq->phead = pq->ptail = NULL;
// 初始化队列大小为0
pq->size = 0;
}
销毁队列
// 销毁队列
void QueueDestroy(Queue* pq)
{
// 断言pq不为空
assert(pq);
// 获取头节点
QNode* cur = pq->phead;
// 遍历链表
while (cur)
{
// 获取下一个节点
QNode* next = cur->next;
// 释放当前节点内存
free(cur);
// 移动cur到下一个节点
cur = next;
}
// 头节点和尾节点置为空
pq->phead = pq->ptail = NULL;
// 队列大小置为0
pq->size = 0;
}
入队列
// 向队列中添加元素
void QueuePush(Queue* pq, QDataType x)
{
// 参数检查
assert(pq);
// 分配内存
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc");
return;
}
// 初始化节点
newnode->val = x;
newnode->next = NULL;
// 如果队列不为空,将新节点添加到队列尾部
if (pq->ptail)
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
// 如果队列为空,将新节点作为头节点和尾节点
else
{
pq->phead = pq->ptail = newnode;
}
// 队列大小加1
pq->size++;
}
出队列
// 弹出队列元素
void QueuePop(Queue* pq)
{
// 断言pq不为空
assert(pq);
// 断言头节点不为空
assert(pq->phead != NULL);
// 如果头节点的下一个节点为空,说明只有一个节点,直接释放头节点,并将头节点和尾节点置为空
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
// 如果头节点的下一个节点不为空,说明队列中至少有两个节点,将头节点指向下一个节点,释放当前头节点
else
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
// 队列大小减一
pq->size--;
}
获取队列首元素
// 获取队列首元素
QDataType QueueFront(Queue* pq)
{
// 断言pq不为空
assert(pq);
// 断言pq的队头指针不为空
assert(pq->phead != NULL);
// 返回队列首元素的值
return pq->phead->val;
}
获取队列尾部数据
// 函数:返回队列尾部的数据
QDataType QueueBack(Queue* pq)
{
// 断言:pq不为空
assert(pq);
// 断言:pq的尾部指针不为空
assert(pq->ptail != NULL);
// 返回:pq的尾部数据
return pq->ptail->val;
}
获取队列大小
// 获取队列的大小
int QueueSize(Queue* pq)
{
// 断言pq不为空
assert(pq);
// 返回队列的大小
return pq->size;
}
检查队列是否为空
// 检查队列是否为空
bool QueueEmpty(Queue* pq)
{
// 断言pq不为空
assert(pq);
// 如果队列大小为0,则队列空
return pq->size == 0;
}
2.3Test.c
#include"Queue.h"
int main()
{
// 创建一个队列
Queue q;
// 初始化队列
QueueInit(&q);
// 向队列中添加元素
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
QueuePush(&q, 5);
// 打印队列头部元素
printf("%d ", QueueFront(&q));
// 弹出队列头部元素
QueuePop(&q);
// 当队列不为空时,循环执行
while (!QueueEmpty(&q))
{
// 打印队列头部元素
printf("%d ", QueueFront(&q));
// 弹出队列头部元素
QueuePop(&q);
}
// 销毁队列
QueueDestroy(&q);
// 返回0
return 0;
}