前言:
队的实现所需要的基本操作主要有初始化,销毁释放,判断队空,入队,出队,队的元素个数,返回队头,返回队尾。
因此,我们可以通过三个文件来实现一个队列。其中:
头文件-Queue.h:主要用来实现所需函数的声明以及include的调用。
源文件-Queue.c:主要用来实现所需函数的定义。
源文件-Test.c:主要用于检测和调用各个函数。
头文件-Queue.h:
对于 头文件-Queue.h 我们主要目的是在该文件中实现 初始化,销毁释放,判断队空,入队,出队,队的元素个数,返回队头,返回队尾 这些函数的声明。我们还需在头文件内定义一下结构体的内部结构当然也可以通过typedef使结构体名字简化,这里我是通过链表来实现的队列,所以这里我额外定义了一个结构体用来存储队头、队尾指针。除此之外,我们还需要调用各个函数定义所需要的头文件。如 stdio.h , stdlib.h , assert.h ,stdbool.h 等。
代码如下:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Queue;
void QueueInit(Queue* pq); //初始化
void QueueDestory(Queue* pq); //释放销毁
void QueuePush(Queue* pq,QDataType x); //入队
void QueuePop(Queue* pq); //出队
int QueueSize(Queue* pq); //元素个数
bool QueueEmpty(Queue* pq); //判断队空
QDataType QueueFront(Queue* pq); //队头数据
QDataType QueueBack(Queue* pq); //队尾数据
源文件-Queue.c:
初始化-QueueInit:
这里传入了pq指针因此需断言一下来增强代码的健壮性。我们初始化时需要将结构体中各个元素都赋值为0,指针置空。
代码如下:
void QueueInit(Queue* pq) //初始化
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
释放销毁-QueueDestory:
这里传入了pq指针因此需断言一下来增强代码的健壮性。其中,释放空间的话,我们需要通过借助另一个指针来遍历一遍链表并将遍历过的节点释放置空。最后将head,tail指针置空,将size赋值为0。
代码如下:
void QueueDestory(Queue* pq) //释放销毁
{
assert(pq);
QNode* cur =pq->head;
while (cur)
{
pq->head = cur->next;
free(cur);
cur = NULL;
cur = pq->head;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
判断队空-QueueEmpty:
这里传入了pq指针因此需断言一下来增强代码的健壮性。因为结构体中我们定义了链表长度size,所以这里我们直接返回判断pq->size是否等于0就可以判断是否队空。
代码如下:
bool QueueEmpty(Queue* pq) //判断队空
{
assert(pq);
return pq->size == 0;
}
入队-QueuePush:
这里传入了pq指针因此需断言一下来增强代码的健壮性。然后,这里我们需要自己开辟节点注意空间的开辟要通过perror()函数判断空间是否开辟成功。接着要对新开辟的节点进行一下初始化和赋值。最后,我们需要注意判断一下是否队空(通过head指针为NULL来判断,因为队空时head和tail指针都指向NULL,所以这里我们可以通过断言tail是否为NULL来增强代码的健壮性。),队空的情况下插入我们需将插入的节点直接赋值给head和tail指针即可,若不为空,正常插入即可。
代码如下:
void QueuePush(Queue* pq, QDataType x) //入队
{
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc");
return;
}
newnode->data = x;
newnode->next = NULL;
if (pq->head == NULL)
{
assert(pq->tail==NULL);
pq->head = pq->tail=newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
pq->size++;
}
出队-QueuePop:
这里传入了pq指针因此需断言一下来增强代码的健壮性,另外我们还需断言一下是否队空。然后要注意对于出队后的节点我们要释放避免空间泄露问题。除此之外,还需注意一点就是当代码执行到指针 head指向NULL 时(即这次出队完后队空),我们需将tail指针也置空。
代码如下:
void QueuePop(Queue* pq) //出队
{
assert(pq);
assert(!QueueEmpty(pq));
QNode* del = pq->head;
pq->head = pq->head->next;
free(del);
del = NULL;
if (pq->head == NULL)
{
pq->tail = NULL;
}
pq->size--;
}
元素个数-QueueSize:
这里传入了pq指针因此需断言一下来增强代码的健壮性。因为结构体中我们定义了链表长度size,所以这里我们直接返回ps->size即可。
代码如下:
int QueueSize(Queue* pq) //元素个数
{
assert(pq);
return pq->size;
}
队头数据-QueueFront:
这里传入了pq指针因此需断言一下来增强代码的健壮性,另外对于返回队头元素值,我们需断言一下是否队空来增强函数健壮性。然后,直接返回队头head指针指向的值即可。
代码如下:
QDataType QueueFront(Queue* pq) //队头数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
队尾数据-QueueBack:
这里传入了pq指针因此需断言一下来增强代码的健壮性,另外对于返回队尾元素值,我们需断言一下是否队空来增强函数健壮性。然后,直接返回队尾 tail指针指向的值即可。
代码如下:
QDataType QueueBack(Queue* pq) //队尾数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
源文件-Test.c:
入队、个数检测:
出队检测:
这里第一个图片检测展示的是出次数过多,队空的时候还出队的情况下,断言的作用。第二张图片展示的是正常的出队。
.
队头、队尾元素检测:
结语:
上述内容,即是我个人对简单队列基本操作的实现和见解。若有大佬发现哪里有问题可以私信或评论指教一下我这个小萌新。非常感谢各位友友们的点赞,关注,收藏与支持,我会更加努力的学习编程语言,还望各位多多关照,让我们一起进步吧!