首页/数据结构/03-stack-queue-array/队列的基本操作 🔗 在 Obsidian 中打开
数据结构 · 03-stack-queue-array

队列的基本操作

难度 ★★★重要度 ★★★★★ 考查频率 高题型 选择 / 算法 队列循环队列链队列FIFO
速查
队列是只允许在一端(队尾)插入、另一端(队头)删除的线性表,遵循 FIFO。循环队列用取模 % MaxSize 消除假溢出;长度 (rear − front + MaxSize) % MaxSize,队满(牺牲一单元)(rear+1)%MaxSize == front

核心概念

定义

队列是只允许在一端(队尾)插入,另一端(队头)删除的线性表。

  • FIFO:先进先出(First In First Out)
  • 队头(Front):允许删除的一端
  • 队尾(Rear):允许插入的一端

一、顺序队列(循环队列)

问题:假溢出

普通顺序队列中,frontrear 不断后移,即使前面有空位也无法使用。

解决:循环队列

将数组首尾相连,形成逻辑上的环。

#define MaxSize 50
typedef struct {
    ElemType data[MaxSize];
    int front;  // 队头指针
    int rear;   // 队尾指针
} SqQueue;

初始化

void InitQueue(SqQueue *Q) {
    Q->front = 0;
    Q->rear = 0;
}

判空

bool QueueEmpty(SqQueue Q) {
    return (Q.front == Q.rear);
}

入队

bool EnQueue(SqQueue *Q, ElemType x) {
    if ((Q->rear + 1) % MaxSize == Q->front)  // 队满
        return false;
    Q->data[Q->rear] = x;
    Q->rear = (Q->rear + 1) % MaxSize;
    return true;
}

出队

bool DeQueue(SqQueue *Q, ElemType *x) {
    if (Q->front == Q->rear)  // 队空
        return false;
    *x = Q->data[Q->front];
    Q->front = (Q->front + 1) % MaxSize;
    return true;
}

队列长度

int QueueLength(SqQueue Q) {
    return (Q.rear - Q.front + MaxSize) % MaxSize;
}

判满方法(三种)

方法 1:牺牲一个存储单元

  • 队满:$ (rear + 1) \% MaxSize == front $
  • 队空:$ front == rear $
  • 元素个数:$(rear - front + MaxSize) \% MaxSize$
  • 可用空间:$MaxSize - 1$

方法 2:增加 size 变量

typedef struct {
    ElemType data[MaxSize];
    int front, rear, size;
} SqQueue;
// 队空:size == 0
// 队满:size == MaxSize

方法 3:增加 tag 变量

typedef struct {
    ElemType data[MaxSize];
    int front, rear, tag;
} SqQueue;
// 最近操作:tag=1 为入队,tag=0 为出队
// 队空:front == rear 且 tag == 0
// 队满:front == rear 且 tag == 1

二、链队列

typedef struct LinkNode {
    ElemType data;
    struct LinkNode *next;
} LinkNode;

typedef struct {
    LinkNode *front, *rear;  // 队头队尾指针
} LinkQueue;

初始化(带头结点)

void InitQueue(LinkQueue *Q) {
    Q->front = Q->rear = (LinkNode *)malloc(sizeof(LinkNode));
    Q->front->next = NULL;
}

判空

bool QueueEmpty(LinkQueue Q) {
    return (Q.front == Q.rear);
}

入队(尾插法)

void EnQueue(LinkQueue *Q, ElemType x) {
    LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
    s->data = x;
    s->next = NULL;
    Q->rear->next = s;
    Q->rear = s;
}

出队

bool DeQueue(LinkQueue *Q, ElemType *x) {
    if (Q->front == Q->rear) return false;
    LinkNode *p = Q->front->next;
    *x = p->data;
    Q->front->next = p->next;
    if (Q->rear == p)  // 若只有一个元素
        Q->rear = Q->front;
    free(p);
    return true;
}
注意出队时若删除的是最后一个元素,rear 需要指向头结点。

销毁

void DestroyQueue(LinkQueue *Q) {
    while (Q->front != NULL) {
        LinkNode *p = Q->front;
        Q->front = Q->front->next;
        free(p);
    }
}

关键对比

总览两者入队/出队均为 O(1);循环队列判满靠取模,链队列动态分配无队满。
特性循环队列链队列
存储方式连续离散
队满判断$ (rear+1)\%MaxSize==front $无(动态分配)
空间固定动态
入队出队O(1)O(1)

手算示例

例 1:循环队列操作

$MaxSize = 6$,初始 $front = rear = 0$

操作frontrear队列内容
EnQueue(A)01[A]
EnQueue(B)02[A,B]
EnQueue(C)03[A,B,C]
DeQueue()13[B,C]
DeQueue()23[C]
EnQueue(D)24[C,D]
EnQueue(E)25[C,D,E]
EnQueue(F)20[C,D,E,F](循环!)

此时 $ (rear+1)\%6 = 1 \neq front=2 $,未满。再入队 G:$ (0+1)\%6 = 1 \neq 2 $,入队后 $rear=1$。再入队:$ (1+1)\%6 = 2 == front $ → 队满!

例 2:队列长度计算

$front = 3,\; rear = 7,\; MaxSize = 8$ → 长度 $ = (7 - 3 + 8) \% 8 = 4$。

常见考法

高频设问
  • 队满/队空条件:牺牲一单元时,队满 $ (rear+1)\%MaxSize == front $,队空 $ front==rear $。
  • 队列长度:$ front=4, rear=1, MaxSize=8 $ → $ (1-4+8)\%8 = 5 $。
  • 操作序列模拟:给定入队/出队序列,写出队列状态。
  • 三种判满方法对比:牺牲空间 / 增加 size / 增加 tag。

易错点

必记
  1. rear 指向下一个空位:rear 指向的是下一个要插入的位置。
  2. 取模运算:front 和 rear 移动都要 % MaxSize
  3. 牺牲一个单元:实际可用空间是 MaxSize-1
  4. 链队列出队:删除最后一个元素时 rear 要指回头结点。
  5. 队列长度公式:$(rear - front + MaxSize) \% MaxSize$,不是 rear - front

核心结论

必背
  1. 循环队列:解决假溢出,利用取模实现循环。
  2. 判满三法:牺牲空间 / 增加 size / 增加 tag。
  3. 链队列:带头结点,头删尾插。
  4. 队列长度:$(rear - front + MaxSize) \% MaxSize$。

记忆卡片

循环队列队空/队满条件(牺牲一单元)?
队空 $front==rear$;队满 $(rear+1)\%MaxSize==front$。
循环队列长度公式?
$(rear-front+MaxSize)\%MaxSize$。
链队列出队何时修改 rear?
删除最后一个元素时,rear 指回头结点。
为什么叫"循环"队列?
数组首尾相连,rear 可从 MaxSize-1 回到 0。
队列与栈的主要区别?
栈 LIFO(后进先出),队列 FIFO(先进先出)。
实际可用空间为何是 MaxSize-1?
牺牲一个单元以区分队空与队满。

交互动画 · 循环队列

front rear
MaxSize = 6,初始空队列
点击「入队」添加元素
橙色高亮 = 已占用;front 在上方、rear 在下方指向下一个空位(牺牲一单元判满)。

相关知识点

stack-basic-operations stack-and-queue-applications circular-queue-full-and-length

↑ 以上为站内 HTML 相对链接(纯网页可浏览);本页右上「在 Obsidian 中打开」跳回源笔记。