C语言队列详解
一、什么是队列?
队列(Queue)是一种先进先出(FIFO, First In First Out)的线性数据结构。它只允许在一端插入数据(队尾),在另一端删除数据(队头)。常见于排队、消息缓冲等场景。
二、队列的结构定义
在C语言中,常用链式结构实现队列。每个节点包含数据域和指向下一个节点的指针。
// 队列节点结构体
typedef int QDataType;
typedef struct QueueNode {QDataType data;struct QueueNode* next;
} QueueNode;
// 队列结构体
typedef struct Queue {QueueNode* phead; // 队头指针QueueNode* ptail; // 队尾指针
} Queue;
三、队列的基本操作
1. 初始化队列
// 初始化队列
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;
}
2. 销毁队列
// 销毁队列
void QueueDestory(Queue* pq)
{assert(pq);QueueNode* cur = pq->phead;while (cur){QueueNode* next = cur->next;free(cur);cur = next;}pq->phead = pq->ptail = NULL;
}
3. 入队(队尾插入)
// 队尾入队列
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){printf("malloc fail\n");exit(-1);}newnode->data = x;newnode->next = NULL;if (pq->ptail == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;}
}
4. 出队(队头删除)
// 队头出队列
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));if (pq->phead->next == NULL){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}
}
5. 获取队列长度
// 获取队列中有效元素个数
int QueueSize(Queue* pq)
{assert(pq);// 如果频繁调用这个接口函数,可以在Queue中给一个size记录数据个数int n = 0;QueueNode* cur = pq->phead;while (cur){n++;cur = cur->next;}return n;
}
6. 获取队头元素
// 获取队列头部元素
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}
7. 获取队尾元素
// 获取队列队尾元素
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;
}
8. 判断队列是否为空
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->ptail == NULL && pq->phead == NULL;
}
四、完整示例
头文件:
#pragma once
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>typedef int QDataType;
typedef struct QueueNode
{struct QueueNode* next;QDataType data;
}QueueNode;typedef struct Queue
{QueueNode* phead;//头指针QueueNode* ptail;//尾指针
}Queue;
// 初始化队列
void QueueInit(Queue* pq);
// 销毁队列
void QueueDestory(Queue* pq);
// 队尾入队列
void QueuePush(Queue* pq, QDataType x); // 队尾
// 队头出队列
void QueuePop(Queue* pq); // 队头
// 获取队列中有效元素个数
int QueueSize(Queue* pq);
// 获取队列头部元素
QDataType QueueFront(Queue* pq);
// 获取队列队尾元素
QDataType QueueBack(Queue* pq);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq);
.c文件
#include "Queue.h"
// 初始化队列
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;
}
// 销毁队列
void QueueDestory(Queue* pq)
{assert(pq);QueueNode* cur = pq->phead;while (cur){QueueNode* next = cur->next;free(cur);cur = next;}pq->phead = pq->ptail = NULL;
}
// 队尾入队列
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){printf("malloc fail\n");exit(-1);}newnode->data = x;newnode->next = NULL;if (pq->ptail == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;}
}
// 队头出队列
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));if (pq->phead->next == NULL){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}
}
// 获取队列中有效元素个数
int QueueSize(Queue* pq)
{assert(pq);// 如果频繁调用这个接口函数,可以在Queue中给一个size记录数据个数int n = 0;QueueNode* cur = pq->phead;while (cur){n++;cur = cur->next;}return n;
}
// 获取队列头部元素
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->ptail == NULL && pq->phead == NULL;
}
测试文件:
#include "Queue.h"int main()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);printf("%d ", QueueFront(&q));QueuePop(&q);QueuePush(&q, 3);QueuePush(&q, 4);while (!QueueEmpty(&q)){printf("%d ", QueueFront(&q));QueuePop(&q);}printf("\n");return 0;
}
五、注意事项与常见错误
-
内存管理:每次
malloc
后都要free
,防止内存泄漏。 -
判空操作:出队、取队头/队尾元素前要判空,防止访问空指针。
-
队列长度:如需频繁获取队列长度,可在结构体中增加
size
成员,实时维护。 -
断言检查:操作前应检查指针有效性。
六、队列的应用场景
-
任务调度、消息缓冲、广度优先搜索(BFS)、打印队列等。
七、总结
队列是一种常用的数据结构,适合需要按顺序处理数据的场景。掌握其链式实现原理和常见操作,有助于更好地理解数据结构和C语言指针操作。