现在位置: 首页 > C 语言数据结构与算法 > 正文

数据结构 - 队列

队列(Queue)是一种遵循先进先出(First In First Out,FIFO)原则的线性数据结构,可以类比为现实生活中排队买票的场景——最先排队的人最先获得服务。


队列的概念与原理

队列 (Queue) — FIFO 原理示意
队首
front
20
30
队尾
rear
← dequeue (出队)enqueue (入队) →

enqueue:在 rear (队尾) 插入 | dequeue:从 front (队首) 删除 | 复杂度 O(1)

FIFO 验证:入队 10→20→30,出队 10→20→30(先进先出)

队列只允许在一端(队尾)进行插入操作,在另一端(队首)进行删除操作。

这与栈的 LIFO 原则恰好相反。


数组实现队列

实例

#include <stdio.h>
#include <stdbool.h>

#define MAX 100

struct Queue {
    int items[MAX];
    int front;   /* 队首指针:指向第一个元素 */
    int rear;    /* 队尾指针:指向下一个待插入位置 */
};

void initQueue(struct Queue* q) {
    q->front = 0;
    q->rear = 0;
}

bool isEmpty(struct Queue* q) {
    return q->front == q->rear;
}

bool isFull(struct Queue* q) {
    return q->rear == MAX;
}

/* 入队:在队尾添加元素 */
void enqueue(struct Queue* q, int value) {
    if (isFull(q)) {
        printf("队列已满!\n");
        return;
    }
    q->items[q->rear++] = value;  /* 在 rear 处写入,然后 rear 后移 */
    printf("入队: %d\n", value);
}

/* 出队:从队首移除元素 */
int dequeue(struct Queue* q) {
    if (isEmpty(q)) {
        printf("队列为空!\n");
        return -1;
    }
    return q->items[q->front++];  /* 返回 front 处元素,然后 front 后移 */
}

int main() {
    struct Queue q;
    initQueue(&q);

    enqueue(&q, 10);  /* 入队: 10 */
    enqueue(&q, 20);  /* 入队: 20 */
    enqueue(&q, 30);  /* 入队: 30 */

    printf("出队: %d\n", dequeue(&q));  /* 输出: 出队: 10 (先进先出) */
    printf("出队: %d\n", dequeue(&q));  /* 输出: 出队: 20 */
    printf("出队: %d\n", dequeue(&q));  /* 输出: 出队: 30 */
    return 0;
}

普通数组实现的队列存在"假溢出"问题:随着不断的入队和出队,front 和 rear 持续向数组末尾移动,即使数组前面已有大量空位也无法再利用。解决方案是使用循环队列


循环队列

循环队列 — 取模运算实现指针回绕

循环队列关键公式

入队: items[rear] = value; rear = (rear + 1) % MAX;

出队: value = items[front]; front = (front + 1) % MAX;

判空: front == rear | 判满: (rear + 1) % MAX == front

运行示例 (容量=5, 最多存4个):入队 10→20→30→40, 出队 10→20, 入队 50→60 → 60 存入索引 0(回绕)完美利用释放空间

循环队列将数组在逻辑上视为首尾相连的环形结构,通过取模运算实现指针回绕。

实例

#include <stdio.h>
#include <stdbool.h>

#define MAX 5  /* 故意设置较小的容量,便于观察循环效果 */

struct CircularQueue {
    int items[MAX];
    int front;  /* 队首索引 */
    int rear;   /* 队尾索引(下一个插入位置)*/
};

void initQueue(struct CircularQueue* q) {
    q->front = 0;
    q->rear = 0;
}

/* 判空:front 和 rear 重合 */
bool isEmpty(struct CircularQueue* q) {
    return q->front == q->rear;
}

/* 判满:(rear + 1) % MAX == front
   注意:循环队列故意空一个位置,用于区分空和满 */

bool isFull(struct CircularQueue* q) {
    return (q->rear + 1) % MAX == q->front;
}

/* 入队:在 rear 处写入,然后 rear 循环后移 */
void enqueue(struct CircularQueue* q, int value) {
    if (isFull(q)) {
        printf("循环队列已满!\n");
        return;
    }
    q->items[q->rear] = value;
    q->rear = (q->rear + 1) % MAX;  /* 取模实现回绕 */
    printf("入队: %d (rear=%d)\n", value, q->rear);
}

/* 出队:返回 front 处元素,然后 front 循环后移 */
int dequeue(struct CircularQueue* q) {
    if (isEmpty(q)) {
        printf("循环队列为空!\n");
        return -1;
    }
    int value = q->items[q->front];
    q->front = (q->front + 1) % MAX;  /* 取模实现回绕 */
    return value;
}

int main() {
    struct CircularQueue q;
    initQueue(&q);

    enqueue(&q, 10);  /* 入队: 10 (rear=1) */
    enqueue(&q, 20);  /* 入队: 20 (rear=2) */
    enqueue(&q, 30);  /* 入队: 30 (rear=3) */
    enqueue(&q, 40);  /* 入队: 40 (rear=4) — 容量 5,最多存 4 个 */

    printf("出队: %d\n", dequeue(&q));  /* 出队: 10 (front=1) */
    printf("出队: %d\n", dequeue(&q));  /* 出队: 20 (front=2) */

    enqueue(&q, 50);  /* 入队: 50 (rear=0 — 回绕到开头!) */
    enqueue(&q, 60);  /* 入队: 60 (rear=1) */

    printf("剩余元素: ");
    while (!isEmpty(&q)) {
        printf("%d ", dequeue(&q));
    }
    printf("\n");  /* 输出: 剩余元素: 30 40 50 60 */
    return 0;
}

队列的应用场景

应用场景说明
广度优先搜索(BFS)按层次逐层遍历图的节点,需要队列来存储"待访问"的节点
操作系统任务调度CPU 按任务到达的先后顺序分配时间片
打印任务队列多台电脑共享一台打印机,打印请求按提交顺序排队处理
消息队列分布式系统中,生产者产生的消息通过队列传递给消费者