一、队列基础
1.基本定义
队列是一种先进先出(FIFO - First In First Out) 的线性数据结构。元素只能从队尾(rear/tail) 添加,从队头(front/head) 移除。

2.核心特性
-
操作限制
- 入队(Enqueue/Push):只能在队尾添加元素
- 出队(Dequeue/Pop):只能在队头移除元素
-
类比现实
- 排队场景:就像现实生活中的排队,先来的人先服务,后来的人排在队尾。
3.常见应用场景
- 任务调度:操作系统进程调度
- 消息队列:消息传递系统
- 广度优先搜索:BFS算法
- 缓冲区管理:打印任务排队
- 数据流处理:实时数据处理
4.特殊变种
- 双端队列(Deque):两端都可插入删除
- 循环队列:数组实现,充分利用空间
- 优先队列:元素按优先级出队
- 阻塞队列:队列空/满时阻塞操作
二、代码实现
- C++标准库中的队列(queue)是一个容器适配器,提供了很多接口,常见的有入队,出队,判空,获取队头队尾,获取节点个数等。我们来逐一实现。
下面是原代码及部分注释。
1.Queue.h
c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
//定义队列节点
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* pnext;
QDataType x;
}QNode;
//定义队列
typedef struct Queue
{
struct QueueNode* head;//队头
struct QueueNode* ptail;//队尾
int size;
}Queue;
//初始化队列
void QueueInit(Queue* ps);
//销毁队列
void QueueDestory(Queue* ps);
//入队列
void QueuePush(Queue* ps, QDataType x);
//出队列
void QueuePop(Queue* ps);
//获取队头数据
QDataType QueueFront(Queue* ps);
//获取队尾数据
QDataType QueueBack(Queue* ps);
//获取队列中有效元素个数
int QueueSize(Queue* ps);
//队列判空
bool QueueEmpty(Queue* ps);
//队列的打印
void QueuePrint(const Queue* ps);
- 我们选择使用列表来实现队列,其中首先创建了一个节点结构体,但与队列不同的是:我们还创建了一个队列结构体,这样做的原因是:
- 入队时只能在尾部入队,保存尾节点方便操作
- 保存size可以直接进行获取元素个数,判空等操作
- 根据队列的特点,我们发现定义一个队列结构体我们会方便很多。
2.Qnene.c
c
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"
//初始化队列
void QueueInit(Queue* ps)
{
assert(ps);
ps->ptail = ps->head = NULL;
ps->size = 0;
}
//销毁队列
void QueueDestory(Queue* ps)
{
assert(ps);
while (ps->size)
{
QNode* pFree = ps->head;
ps->head = ps->head->pnext;
free(pFree);
ps->size--;
}
ps->head = NULL;
ps->ptail = NULL;
}
//入队列
void QueuePush(Queue* ps, QDataType x)
{
assert(ps);
QNode* pcur = (QNode*)malloc(sizeof(QNode));
pcur->x = x;
//空队列情况
if (ps->size == 0)
{
ps->head = ps->ptail = pcur;
pcur->pnext = NULL;
ps->size++;
return;
}
ps->ptail->pnext = pcur;
ps->ptail= pcur;
pcur->pnext = NULL;
ps->size++;
}
//出队列
void QueuePop(Queue* ps)
{
assert(ps);
assert(ps->size);
//如果队列只有一个节点
if (ps->size == 1)
{
free(ps->head);
ps->ptail = ps->head = NULL;
ps->size--;
return;
}
QNode* pFree = ps->head;
ps->head = ps->head->pnext;
free(pFree);
ps->size--;
}
//获取队头数据
QDataType QueueFront(Queue* ps)
{
assert(ps);
assert(ps->head);
return ps->head->x;
}
//获取队尾数据
QDataType QueueBack(Queue* ps)
{
assert(ps);
assert(ps->ptail);
return ps->ptail->x;
}
//获取队列中有效元素个数
int QueueSize(Queue* ps)
{
assert(ps);
return ps->size;
}
//队列判空
bool QueueEmpty(Queue* ps)
{
assert(ps);
return ps->size==0;
}
//队列的打印
void QueuePrint(const Queue* ps)
{
assert(ps);
QNode* current = ps->head;
printf("Head -> ");
while (current != NULL)
{
printf("%d", current->x);
if (current->pnext != NULL)
{
printf(" -> ");
}
current = current->pnext;
}
printf(" -> Tail\n");
}
- 队列的写法与单链表基本相同,不过要注意其中对队列结构体的使用。唯一看的就是判空操作,我们返回ps->size == 0,而这是一个真假值,我们最好用bool类型。如果ps->size ==0,说明队列为空,判空操作返回真值。
3.测试代码
最后,我们对写完的代码进行测试
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include"Queue.h"
int main() {
Queue q;
printf("========== 测试1:初始化队列 ==========\n");
QueueInit(&q);
printf("初始化完成。\n");
QueuePrint(&q);
printf("\n");
printf("========== 测试2:入队操作 ==========\n");
QueuePush(&q, 10);
QueuePrint(&q);
QueuePush(&q, 20);
QueuePrint(&q);
QueuePush(&q, 30);
QueuePrint(&q);
QueuePush(&q, 40);
QueuePrint(&q);
printf("\n");
printf("========== 测试3:获取队列信息 ==========\n");
printf("队列大小: %d\n", QueueSize(&q));
printf("队列是否为空: %s\n", QueueEmpty(&q) ? "是" : "否");
printf("队头元素: %d\n", QueueFront(&q));
printf("队尾元素: %d\n", QueueBack(&q));
printf("当前队列状态: ");
QueuePrint(&q);
printf("\n");
printf("========== 测试4:出队操作 ==========\n");
printf("出队操作\n");
QueuePop(&q);
printf("出队后队列: ");
QueuePrint(&q);
printf("新队头元素: %d\n", QueueFront(&q));
printf("队列大小: %d\n", QueueSize(&q));
printf("\n");
printf("再出队一次\n");
QueuePop(&q);
printf("出队后队列: ");
QueuePrint(&q);
printf("队列大小: %d\n", QueueSize(&q));
printf("\n");
printf("========== 测试5:边界情况测试 ==========\n");
printf("继续出队直到队列为空...\n");
printf("第三次出队\n");
QueuePop(&q);
printf("队列状态: ");
QueuePrint(&q);
printf("第四次出队\n");
QueuePop(&q);
printf("队列状态: ");
QueuePrint(&q);
printf("队列是否为空: %s\n", QueueEmpty(&q) ? "是" : "否");
printf("队列大小: %d\n", QueueSize(&q));
printf("\n");
printf("========== 测试6:再次入队测试 ==========\n");
printf("重新入队5个元素\n");
for (int i = 1; i <= 5; i++) {
printf("入队元素: %d\n", i * 100);
QueuePush(&q, i * 100);
QueuePrint(&q);
}
printf("\n========== 测试7:最终队列信息 ==========\n");
printf("队列大小: %d\n", QueueSize(&q));
printf("队头元素: %d\n", QueueFront(&q));
printf("队尾元素: %d\n", QueueBack(&q));
printf("最终队列状态: ");
QueuePrint(&q);
printf("\n");
printf("========== 测试8:销毁队列 ==========\n");
QueueDestory(&q);
printf("队列销毁完成。\n");
printf("所有测试完成!\n");
return 0;
}

- 最后显示测试通过,代表我们代码没有错误。
三、队列与栈的对比
| 队列(Queue) | 栈(Stack) | |
|---|---|---|
| 顺序 | 先进先出(FIFO) | 后进先出(LIFO) |
| 插入 | 队尾(Rear) | 栈顶(Top) |
| 删除 | 队头(Front) | 栈顶(Top) |
| 类比 | 排队 | 盘子堆叠 |
四、结语
以上是队列的简介、实现及与栈的对比,如有错误,欢迎指正~