队列 - 先进先出的线性结构

什么是队列?

📰 5W1H 发明者故事

Who(何人)- 发明者是谁?

发明者:约翰·冯·诺依曼(John von Neumann)和早期计算机科学家们
背景:冯·诺依曼(1903-1957),匈牙利裔美国数学家,普林斯顿高等研究院教授,被誉为"现代计算机之父"。他的博学令人叹为观止——从集合论到量子力学,从博弈论到弹道计算,无所不通。

当时的处境:1950年代,计算机开始被用于处理批量任务。程序员发现,很多现实问题——打印机作业排队、网络包缓冲、进程调度——都需要一种能按顺序"公平"处理的数据结构。冯·诺依曼在设计存储程序计算机时,就意识到了"有序等待"这一模式的重要性。

When(何时)- 什么时候发明的?

时间:1940年代末—1950年代初
时代背景:

  • 冯·诺依曼体系结构(1945年)刚刚确立
  • 早期操作系统雏形出现,批处理作业排队是核心问题
  • 计算机从军用研究走向商业应用(IBM 701,1952年)
  • 多任务、多用户计算的需求开始出现
  • "先到先服务"被认为是最公平的调度原则

Where(何地)- 在哪里发明的?

地点:美国新泽西州普林斯顿高等研究院(IAS)
环境:二战后的美国,科学研究经费充裕,政府希望计算机能处理大量科学计算和行政事务。IAS计算机项目汇聚了当时最聪明的数学家和工程师,他们既要解决硬件问题,也要解决软件调度问题。

What(何事)- 发明了什么?

数据结构:队列(Queue)
核心概念:像排队买票一样,先到的先服务——“先进先出”(FIFO: First In First Out)。新来的人排在队伍尾部,服务完成后从队伍头部离开。
关键突破:

  • 公平性原则:保证数据按到达顺序被处理,不发生饥饿
  • 双端管理:一端(队尾)负责入队,另一端(队头)负责出队
  • 缓冲器模型:生产者-消费者之间的解耦机制
  • 循环实现:用环形数组避免空间浪费

Why(何因)- 为什么发明?

要解决的问题:

  1. 批处理调度:多个程序等待CPU执行,必须有一种公平的排队机制
  2. I/O缓冲:打印机、磁带读写速度远低于CPU,需要缓冲队列
  3. 广度优先搜索:图遍历时需要按层次顺序访问节点
  4. 网络数据包:路由器需要按到达顺序转发数据包

当时的挑战:

  • 内存极其稀缺,不能为队列预留太多空间
  • 数组实现的队列会产生"假满"问题(数据不断后移,头部浪费)
  • 需要O(1)的入队和出队操作

动机:现实世界中大量的"等待"模型——银行排队、工厂流水线、邮局取号——都符合先进先出的规律。将这种日常逻辑形式化为数据结构,是一个自然而然的抽象。

How(何果)- 如何实现?有什么影响?

实现思路:

  • 用数组存储元素,维护头指针(front)和尾指针(rear)
  • 入队:rear指针加1,存入数据
  • 出队:取出front处数据,front指针加1
  • 循环队列:用取模运算避免数组越界,实现"环形"复用

技术方案:

循环队列(容量5):
索引:  [0] [1] [2] [3] [4]
数据:  [A] [B] [C] [ ] [ ]
            ↑           ↑
          front        rear
入队D:rear=(3+1)%5=3,data[3]=D
出队:取data[1]=B,front=(1+1)%5=2

历史影响:

  • 操作系统的进程调度队列(就绪队列、等待队列)
  • 网络协议栈的数据包缓冲
  • 广度优先搜索(BFS)的核心数据结构
  • 生产者-消费者模型的基础
  • 消息队列系统(RabbitMQ、Kafka)的理论根基

今天的使用:

  • CPU任务调度(时间片轮转)
  • 键盘/鼠标事件队列
  • Web服务器的请求队列
  • 打印机缓冲队列

📝 自然语言需求定义

需求名称:实现循环队列,支持先进先出(FIFO)操作

功能需求(用精确的中文描述)

  1. 初始化队列:创建一个指定容量的空队列

    • 输入:最大容量(正整数)
    • 操作:分配内存,初始化头尾指针
    • 输出:队列指针,失败返回NULL
  2. 入队(Enqueue):将元素加入队尾

    • 输入:队列指针,要加入的整数
    • 操作:检查队列是否已满,未满则在rear位置存入数据,rear前进一位
    • 输出:成功返回true,失败(队满)返回false
  3. 出队(Dequeue):从队头取出元素

    • 输入:队列指针
    • 操作:检查队列是否为空,非空则取出front处数据,front前进一位
    • 输出:成功返回true,数据通过指针参数返回;失败返回false
  4. 查看队头(Front):查看但不取出队头元素

    • 输入:队列指针
    • 操作:返回front处的元素值,不移除
    • 输出:成功返回true;失败(队空)返回false
  5. 获取队列大小:返回当前队列中元素个数

    • 输入:队列指针
    • 输出:元素个数,空队列返回0

约束条件

  • 使用循环数组(取模运算)实现,避免假满问题
  • 队满条件:(rear + 1) % capacity == front
  • 队空条件:front == rear
  • 所有操作时间复杂度O(1)
  • 循环队列实际存储容量为 capacity-1(预留一格区分满/空)

验收标准(必须可验证)

编号 测试场景(自然语言描述) 预期结果 验证方式
1 创建容量为5的队列 队列为空,大小为0 检查is_empty和size
2 入队10,20,30 队列大小为3,队头为10 检查size和front值
3 出队一次 返回10,新队头为20,大小变为2 检查返回值、front和size
4 队列填满后继续入队 返回false,队列内容不变 填满后再enqueue,检查返回值
5 空队列出队 返回false,不崩溃 空队列dequeue,检查返回值
6 先出队再入队(利用循环特性) 正确利用前段空间,操作成功 出队2次后再入队,验证循环复用
7 连续入队出队直到为空 最终队列为空,所有数据顺序正确 批量操作验证FIFO属性

AI 生成提示

基于以上需求和验收标准,用标准C语言实现循环队列。

要求:
1. 使用标准C99
2. 结构体包含:data数组、front、rear、capacity字段
3. 用取模运算实现循环特性
4. 包含完整错误处理(NULL检查、满/空检查)
5. 内存安全(malloc/free配对)
6. 代码必须有详细注释
7. 在main函数中实现所有7个验收标准的测试用例

核心函数:
- init_queue(capacity) - 初始化
- enqueue(queue, value) - 入队
- dequeue(queue, &value) - 出队
- front(queue, &value) - 查看队头
- is_empty(queue) - 是否为空
- is_full(queue) - 是否已满
- queue_size(queue) - 获取大小
- free_queue(queue) - 释放内存

💻 C语言实现文件

对应文件: queue.c

编译运行:

gcc -o queue_test queue.c
./queue_test

核心函数:

  • init_queue(capacity) - 初始化循环队列
  • enqueue(queue, value) - 入队
  • dequeue(queue, &value) - 出队
  • front_val(queue, &value) - 查看队头
  • is_empty(queue) - 判断是否为空
  • queue_size(queue) - 获取大小
  • free_queue(queue) - 释放内存
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐