建议先阅读: A 容器 Container, B 栈 Stack


原理

队列(Queue)是一种受限的线性数据结构,遵循先进先出(FIFO, First In First Out)原则。元素只能从队尾插入,从队首删除。

核心操作

操作描述时间复杂度
push / enqueue在队尾插入元素O(1)
pop / dequeue删除队首元素O(1)
front获取队首元素O(1)
back获取队尾元素O(1)
empty判断队列是否为空O(1)
size返回队列中元素个数O(1)

底层实现方式

  • 循环队列(数组): 通过取模运算避免假溢出,固定容量或动态扩容
  • 链表队列: head 指向队首(出队端),tail 指向队尾(入队端),无容量限制
  • 双端队列(deque): 分段连续存储,两端都可插入/删除,也支持随机访问

假溢出与循环队列

普通数组实现中,队首元素出队后空间被浪费,tail 可能到达数组末尾无法入队。循环队列通过 (index + 1) % capacity 使 tail 绕回数组开头,充分利用空间。


实现

循环队列

#include <stdlib.h>
 
typedef struct {
    int* data;
    size_t head;     // 队首位置
    size_t tail;     // 队尾位置(下一个插入位置)
    size_t capacity;
    size_t count;
} CircularQueue;
 
void cq_init(CircularQueue* q, size_t cap) {
    q->data = malloc(cap * sizeof(int));
    q->head = 0;
    q->tail = 0;
    q->capacity = cap;
    q->count = 0;
}
 
void cq_destroy(CircularQueue* q) {
    free(q->data);
}
 
static int cq_resize(CircularQueue* q) {
    size_t new_cap = q->capacity * 2;
    int* new_data = malloc(new_cap * sizeof(int));
    if (!new_data) return -1;
    for (size_t i = 0; i < q->count; i++)
        new_data[i] = q->data[(q->head + i) % q->capacity];
    free(q->data);
    q->data = new_data;
    q->head = 0;
    q->tail = q->count;
    q->capacity = new_cap;
    return 0;
}
 
int cq_push(CircularQueue* q, int value) {
    if (q->count >= q->capacity)
        if (cq_resize(q) != 0) return -1;
    q->data[q->tail] = value;
    q->tail = (q->tail + 1) % q->capacity;
    q->count++;
    return 0;
}
 
int cq_pop(CircularQueue* q) {
    if (q->count == 0) return -1;
    q->head = (q->head + 1) % q->capacity;
    q->count--;
    return 0;
}
 
int cq_front(CircularQueue* q, int* out) {
    if (q->count == 0) return -1;
    *out = q->data[q->head];
    return 0;
}
 
int cq_back(CircularQueue* q, int* out) {
    if (q->count == 0) return -1;
    *out = q->data[(q->tail + q->capacity - 1) % q->capacity];
    return 0;
}
 
int cq_empty(CircularQueue* q) { return q->count == 0; }
size_t cq_size(CircularQueue* q) { return q->count; }

链表队列

#include <stdlib.h>
 
typedef struct QNode {
    int data;
    struct QNode* next;
} QNode;
 
typedef struct {
    QNode* head;  // 队首
    QNode* tail;  // 队尾
    size_t count;
} LinkedQueue;
 
void lq_init(LinkedQueue* q) {
    q->head = NULL;
    q->tail = NULL;
    q->count = 0;
}
 
void lq_destroy(LinkedQueue* q) {
    while (q->head) {
        QNode* tmp = q->head;
        q->head = q->head->next;
        free(tmp);
    }
    q->tail = NULL;
    q->count = 0;
}
 
int lq_push(LinkedQueue* q, int value) {
    QNode* node = malloc(sizeof(QNode));
    if (!node) return -1;
    node->data = value;
    node->next = NULL;
    if (q->tail)
        q->tail->next = node;
    else
        q->head = node;
    q->tail = node;
    q->count++;
    return 0;
}
 
int lq_pop(LinkedQueue* q) {
    if (!q->head) return -1;
    QNode* tmp = q->head;
    q->head = q->head->next;
    if (!q->head) q->tail = NULL;
    free(tmp);
    q->count--;
    return 0;
}
 
int lq_front(LinkedQueue* q, int* out) {
    if (!q->head) return -1;
    *out = q->head->data;
    return 0;
}
 
int lq_back(LinkedQueue* q, int* out) {
    if (!q->tail) return -1;
    *out = q->tail->data;
    return 0;
}
 
int lq_empty(LinkedQueue* q) { return q->head == NULL; }
size_t lq_size(LinkedQueue* q) { return q->count; }

各语言标准库对比

语言队列双端队列
C无(手写)无(手写)
C++queue(deque 封装)deque
JavaLinkedList / ArrayDequeArrayDeque
Pythoncollections.dequecollections.deque
RustVecDequeVecDeque

应用场景

  • 广度优先搜索(BFS): 层序遍历树/图,先发现的节点先处理
  • 消息队列: 生产者-消费者模型,异步通信与解耦
  • CPU 任务调度: 就绪队列按 FIFO 分配时间片
  • 滑动窗口: 用单调队列维护窗口内的最大值/最小值

单调队列求滑动窗口最大值

核心思想:维护一个递减队列,队头始终是当前窗口的最大值。每次窗口右移时,移除过期元素(出左边),加入新元素(踢掉比它小的队尾),队头即为答案。

flowchart LR
    A["窗口右移一步"] --> B["移除左端出界元素"]
    B --> C{"新元素 x > 队尾元素?"}
    C -->|是| D["弹出队尾"]
    D --> C
    C -->|否| E["x 入队尾"]
    E --> F["队头即为窗口最大值"]
// 返回结果需要调用者 free
int* max_sliding_window(const int* nums, int n, int k, int* result_size) {
    int* result = malloc((n - k + 1) * sizeof(int));
    int* dq = malloc(n * sizeof(int));  // 存下标,队头到队尾递减
    int head = 0, tail = 0;
    int ri = 0;
    for (int i = 0; i < n; i++) {
        // 移除超出窗口的队头
        while (tail > head && dq[head] <= i - k)
            head++;
        // 保持递减
        while (tail > head && nums[dq[tail - 1]] <= nums[i])
            tail--;
        dq[tail++] = i;
        if (i >= k - 1)
            result[ri++] = nums[dq[head]];
    }
    *result_size = ri;
    free(dq);
    return result;
}

练习

题号题目难度知识点
P1540机器翻译入门队列模拟
P1996约瑟夫问题入门队列模拟
P1886滑动窗口普及+单调队列