纯C语言实现,不涉及C++
链队列
队列的链式表示称为链队列,它实际上是一个同时具有队头指针和队尾指针的单链表,头指针指向对头结点,尾指针指向队尾结点。
头结点
是链式队列中的特殊结点,通常不存储实际的队列元素数据,其主要作用是方便对队列的操作,例如在进行入队、出队操作时,可以统一操作逻辑,无需特殊处理队列为空的情况。它作为队列的头部标识,其next指针指向队列中的第一个真正存储数据的结点。
尾结点(注意区分头结点)
在链式队列中,尾结点是最后一个存储有效数据的结点,它的next指针被设置为NULL
链式队列基本操作实现
0. 存储结构声明
cs
typedef int ElemType;
typedef struct Linknode {
ElemType data; //数据域
struct Linknode* next; //指针域
}Linknode;
typedef struct LinkQueue {
Linknode* front; //队头指针
Linknode* rear; //队尾指针
}LinkQueue;
1. 初始化
cs
void InitQueue(LinkQueue* Q) {
Q->front = Q->rear = (Linknode*)malloc(sizeof(Linknode));
if (Q->front == NULL)
{
printf("内存分配失败!\n");
return;
}
Q->front->next = NULL;
}
2. 判空
cs
int QueueEmpty(LinkQueue* Q) {
return Q->front == Q->rear;
}
3. 入队
cs
void enQueue(LinkQueue* Q, ElemType value) {
//1.申请新结点的内存空间
Linknode* s = (Linknode*)malloc(sizeof(Linknode));
//2.检查内存是否分配成功
if (s == NULL)
{
printf("内存分配失败!\n");
return;
}
//3.设置新结点的数据和指针
s->data = value;
s->next = NULL;
//4.将新结点连接到队列尾部
Q->rear->next = s;
//5.更新队尾指针
Q->rear = s; //将队尾指针 Q->rear 更新为指向新的队尾结点 s,以便在后续的入队操作或其他与队尾相关的操作中能够正确地访问到队尾。
}
4. 出队
cs
int deQueue(LinkQueue* Q) {
//1. 检查队列是否为空
if (QueueEmpty(Q))
{
printf("队列为空,无元素可出队!\n");
return -1;
}
//2.保存对头元素的指针
//创建一个新的指针变量 p,将其指向队头元素的结点(即头结点的下一个结点).因为头结点本身不存储实际的队列元素,所以 Q->front->next 指向的才是真正的队头元素。
Linknode* p = Q->front->next;
//3.调整头结点的next指针
//将头结点的 next 指针指向当前队头元素结点的下一个结点。这样就相当于把原来的队头元素从队列中 "移除" 了,此时头结点的 next 指针指向的结点成为了新的队头元素(如果队列还有其他元素的话)。
Q->front->next = p->next;
//4.处理队列为空的特殊情况
//检查原来的队尾指针 rear 是否指向当前要删除的队头元素结点 p。如果是,说明删除队头元素后队列中没有其他元素了(队列为空),此时需要将 rear 指针重新指向头结点,以保证队列的空状态时 front 和 rear 相等。
if (Q->rear == p)
{
Q->rear = Q->front; // 注意是进行赋值,来更新队尾指针,而不是比较==
}
//5. 释放队头元素结点的内存
//使用 free 函数释放掉之前保存的队头元素结点(即 p 指向的结点)所占用的内存空间,避免内存泄漏。
free(p);
//6. 返回成功的标志
return 0; //出队成功
}
5. 获取队头元素
cs
int getQueueValue(LinkQueue* Q, ElemType* value) {
if (QueueEmpty(Q))
{
printf("队列为空,无元素可出队!\n");
return -1;
}
Linknode* p = Q->front->next;
*value = p->data;
return 0; //查找成功
}
6. 打印
cs
void printQueue(LinkQueue* Q) {
if (QueueEmpty(Q))
{
printf("队列为空,无元素可打印!\n");
return ;
}
Linknode* p = Q->front->next;
printf("队列中的元素为:");
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
7. 注销
cs
void destroyQueue(LinkQueue* Q) {
while (Q->front != NULL) {
Q->rear = Q->front->next;
free(Q->front);
Q->front = Q->rear;
}
}
8. 测试
cs
int main() {
LinkQueue Q;
InitQueue(&Q);
//入队操作测试
enQueue(&Q, 11);
enQueue(&Q, 22);
enQueue(&Q, 33);
printQueue(&Q); //队列中的元素为:11 22 33
//获取对头元素操作测试
ElemType value;
if (getQueueValue(&Q,&value) == 0)
{
printf("即将出队的元素是:%d\n", value); // 即将出队的元素是:11
}
//出队操作测试
if (deQueue(&Q) == 0)
{
printf("出队操作成功!\n"); //出队操作成功!
}
printQueue(&Q); //队列中的元素为:22 33
//判空操作测试
if (QueueEmpty(&Q))
{
printf("队列为空!\n");
}
else {
printf("队列不为空!\n"); //队列不为空!
}
//注销队列
destroyQueue(&Q);
return 0;
}
9. 完整代码
cs
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct Linknode {
ElemType data; //数据域
struct Linknode* next; //指针域
}Linknode;
typedef struct LinkQueue {
Linknode* front; //队头指针
Linknode* rear; //队尾指针
}LinkQueue;
// 操作1------初始化
void InitQueue(LinkQueue* Q) {
Q->front = Q->rear = (Linknode*)malloc(sizeof(Linknode));
if (Q->front == NULL)
{
printf("内存分配失败!\n");
return;
}
Q->front->next = NULL;
}
// 操作2------判空
int QueueEmpty(LinkQueue* Q) {
return Q->front == Q->rear;
}
// 操作3------入队
void enQueue(LinkQueue* Q, ElemType value) {
//1.申请新结点的内存空间
Linknode* s = (Linknode*)malloc(sizeof(Linknode));
//2.检查内存是否分配成功
if (s == NULL)
{
printf("内存分配失败!\n");
return;
}
//3.设置新结点的数据和指针
s->data = value;
s->next = NULL;
//4.将新结点连接到队列尾部
Q->rear->next = s;
//5.更新队尾指针
//将队尾指针 Q->rear 更新为指向新的队尾结点 s,以便在后续的入队操作或其他与队尾相关的操作中能够正确地访问到队尾。
Q->rear = s;
}
// 操作4------出队
int deQueue(LinkQueue* Q) {
//1. 检查队列是否为空
if (QueueEmpty(Q))
{
printf("队列为空,无元素可出队!\n");
return -1;
}
//2.保存对头元素的指针
//创建一个新的指针变量 p,将其指向队头元素的结点(即头结点的下一个结点).因为头结点本身不存储实际的队列元素,所以 Q->front->next 指向的才是真正的队头元素。
Linknode* p = Q->front->next;
//3.调整头结点的next指针
//将头结点的 next 指针指向当前队头元素结点的下一个结点。这样就相当于把原来的队头元素从队列中 "移除" 了,此时头结点的 next 指针指向的结点成为了新的队头元素(如果队列还有其他元素的话)。
Q->front->next = p->next;
//4.处理队列为空的特殊情况
//检查原来的队尾指针 rear 是否指向当前要删除的队头元素结点 p。如果是,说明删除队头元素后队列中没有其他元素了(队列为空),此时需要将 rear 指针重新指向头结点,以保证队列的空状态时 front 和 rear 相等。
if (Q->rear == p)
{
Q->rear = Q->front; // 注意是进行赋值,来更新队尾指针,而不是比较==
}
//5. 释放队头元素结点的内存
//使用 free 函数释放掉之前保存的队头元素结点(即 p 指向的结点)所占用的内存空间,避免内存泄漏。
free(p);
//6. 返回成功的标志
return 0; //出队成功
}
// 操作5------获取队头元素
int getQueueValue(LinkQueue* Q, ElemType* value) {
if (QueueEmpty(Q))
{
printf("队列为空,无元素可出队!\n");
return -1;
}
Linknode* p = Q->front->next;
*value = p->data;
return 0; //查找成功
}
// 操作6------打印链式队列
void printQueue(LinkQueue* Q) {
if (QueueEmpty(Q))
{
printf("队列为空,无元素可打印!\n");
return ;
}
Linknode* p = Q->front->next;
printf("队列中的元素为:");
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
// 操作7------注销链式队列
void destroyQueue(LinkQueue* Q) {
while (Q->front != NULL) {
Q->rear = Q->front->next;
free(Q->front);
Q->front = Q->rear;
}
}
int main() {
LinkQueue Q;
InitQueue(&Q);
//入队操作测试
enQueue(&Q, 11);
enQueue(&Q, 22);
enQueue(&Q, 33);
printQueue(&Q); //队列中的元素为:11 22 33
//获取对头元素操作测试
ElemType value;
if (getQueueValue(&Q,&value) == 0)
{
printf("即将出队的元素是:%d\n", value); // 即将出队的元素是:11
}
//出队操作测试
if (deQueue(&Q) == 0)
{
printf("出队操作成功!\n"); //出队操作成功!
}
printQueue(&Q); //队列中的元素为:22 33
//判空操作测试
if (QueueEmpty(&Q))
{
printf("队列为空!\n");
}
else {
printf("队列不为空!\n"); //队列不为空!
}
//注销队列
destroyQueue(&Q);
return 0;
}
10. 运行截图

分享小妙招:如果对哪个操作不是很明白,就询问AI:请结合以下代码详细描述XXX操作的过程+粘贴的代码
本人菜鸟一只,文章如有错误,欢迎评论区指正!