数据结构——队列

在计算机科学中,队列(Queue)是与栈并驾齐驱的另一种基本线性数据结构。它的核心规则同样可以用四个字概括:先进先出(FIFO, First In First Out)。就像生活中排队购票------先到的人先得到服务,后来的人只能排在队尾,等待前面的人依次离开。

队列在系统设计、算法实现、资源管理等领域扮演着至关重要的角色。本文将从队列的基本概念、核心操作、三种常见实现方式(顺序队列、循环队列、链式队列)、典型应用场景以及复杂度分析等方面,带你全面掌握队列。


一、什么是队列?

队列是一种操作受限 的线性表,它只允许在一端(称为队尾 ,Rear)进行插入操作,在另一端(称为队头 ,Front)进行删除操作。队列中没有元素时称为空队列

可以把队列想象成一根水管:水从一端注入(入队),从另一端流出(出队)。最先注入的水会最先流出,后注入的水只能等前面的水都流出后才能流出。

二、队列的基本操作

队列通常提供以下核心操作:

  • enqueue(item) :将元素 item 插入队尾。

  • dequeue():删除队头元素,并返回该元素。若队列为空,则操作非法。

  • front():获取队头元素,但不删除。

  • is_empty():判断队列是否为空。

  • size():返回队列中元素的个数。

与栈类似,这些操作的时间复杂度在合理实现下均为 O(1)

三、队列的实现方式

队列的实现主要有三种形式:顺序队列(基于数组)循环队列(改进的顺序队列)链式队列(基于链表)

1. 顺序队列(Array-based Queue)

使用数组存储队列元素,并维护两个指针:front 指向队头元素的位置,rear 指向队尾元素的下一个位置(或指向队尾元素,取决于实现习惯)。下面采用 rear 指向下一个插入位置的方式。

python 复制代码
class ArrayQueue:
    def __init__(self, capacity=10):
        self._data = [None] * capacity
        self._capacity = capacity
        self._front = 0   # 队头索引
        self._rear = 0    # 下一个插入位置的索引
        self._size = 0

    def enqueue(self, item):
        if self._size == self._capacity:
            raise OverflowError("Queue is full")
        self._data[self._rear] = item
        self._rear = (self._rear + 1) % self._capacity   # 循环使用数组
        self._size += 1

    def dequeue(self):
        if self.is_empty():
            raise IndexError("dequeue from empty queue")
        item = self._data[self._front]
        self._data[self._front] = None   # 便于垃圾回收
        self._front = (self._front + 1) % self._capacity
        self._size -= 1
        return item

    def front(self):
        if self.is_empty():
            raise IndexError("front from empty queue")
        return self._data[self._front]

    def is_empty(self):
        return self._size == 0

    def size(self):
        return self._size

注意 :上述实现中,当 rear 到达数组末尾时,如果前面有空位,我们可以通过取模运算让 rear 回到开头,实现数组空间的循环利用。这正是循环队列 的思想。单纯的顺序队列如果不采用循环,就会在多次入队出队后出现"假溢出"(即数组未满但 rear 已到末尾)。因此,实际工程中很少使用非循环的顺序队列。

2. 循环队列(Circular Queue)

循环队列是顺序队列的标准形式,它通过取模运算让数组的末尾和开头逻辑上相连,从而充分利用数组空间。判断队列空和满的条件需要谨慎处理。

常用的判断方式有两种:

  • 牺牲一个存储单元,约定 (rear + 1) % capacity == front 为满,front == rear 为空。

  • 增加一个 size 变量记录元素个数(如上代码所示),更直观且不浪费空间。

循环队列的入队、出队操作均只需 O(1) 时间,且没有扩容成本(如果使用固定容量)。若需要动态扩容,可以像动态数组一样在满时创建更大的数组并迁移元素。

3. 链式队列(Linked Queue)

使用链表实现队列,队头对应链表头节点,队尾对应链表尾节点。这种实现没有容量限制,且入队、出队操作都能在 O(1) 内完成。

python 复制代码
class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

class LinkedQueue:
    def __init__(self):
        self._front = None   # 队头指针
        self._rear = None    # 队尾指针
        self._size = 0

    def enqueue(self, item):
        new_node = Node(item)
        if self.is_empty():
            self._front = new_node
            self._rear = new_node
        else:
            self._rear.next = new_node
            self._rear = new_node
        self._size += 1

    def dequeue(self):
        if self.is_empty():
            raise IndexError("dequeue from empty queue")
        item = self._front.value
        self._front = self._front.next
        if self._front is None:   # 队列变空,rear 也应置空
            self._rear = None
        self._size -= 1
        return item

    def front(self):
        if self.is_empty():
            raise IndexError("front from empty queue")
        return self._front.value

    def is_empty(self):
        return self._size == 0

    def size(self):
        return self._size

优点 :动态扩展,无空间浪费(每个节点只有指针开销)。
缺点:每个元素需要额外存储指针,内存占用稍高,且对缓存不友好。

四、队列的复杂度分析

操作 顺序队列(循环) 链式队列
enqueue O(1) * O(1)
dequeue O(1) O(1)
front O(1) O(1)
is_empty O(1) O(1)
size O(1) O(1)

*顺序队列在固定容量下为 O(1);若需要动态扩容,则均摊时间复杂度仍为 O(1)

空间复杂度:顺序队列 O(n)(n 为容量),链式队列 O(m)(m 为实际元素个数,加上指针开销)。

五、队列的经典应用

队列的 FIFO 特性使它天然适合处理"排队"场景,以下是一些典型应用:

1. 任务调度与资源分配

  • 操作系统中的进程调度:就绪队列中的进程按照先来先服务(FCFS)的原则获得 CPU。

  • 打印任务队列:多个用户提交打印任务,打印机按提交顺序依次处理。

2. 缓冲机制

  • 生产者-消费者模型:生产者将数据放入队列,消费者从队列中取出数据,解耦生产与消费速率。例如,消息队列(如 RabbitMQ、Kafka)就是基于队列思想的中间件。

3. 广度优先搜索(BFS)

  • 在图或树的遍历中,BFS 利用队列保存待访问的节点,确保按层次顺序访问。这是解决最短路径、连通性等问题的核心算法。

4. 广度优先的排队问题

  • 键盘缓冲区:当键盘输入速度超过系统处理速度时,输入事件被暂存在队列中,系统依次处理。

  • 网络数据包转发:路由器使用队列缓存待转发的数据包,按顺序发送。

5. 现实世界模拟

  • 银行柜台排队、餐厅叫号系统等都可以用队列建模,用于性能分析和优化。

六、队列的变体

在标准队列的基础上,计算机科学中还衍生出许多特殊队列:

  • 双端队列(Deque) :允许在两端进行插入和删除操作,结合了栈和队列的特性。Python 的 collections.deque 就是高效的双端队列实现。

  • 优先队列(Priority Queue):元素带有优先级,出队时优先级最高的元素先出。通常用堆(Heap)实现,常用于任务调度、Dijkstra 算法等。

  • 阻塞队列(Blocking Queue):在队列为空时,出队操作会阻塞等待;在队列满时,入队操作会阻塞等待。多线程编程中常用。

七、队列的局限与思考

队列的 FIFO 特性在需要严格按顺序处理的场景中非常合适,但它无法随意访问中间元素,也不支持栈那样的"后进先出"行为。选择队列还是其他数据结构,需要根据实际需求来定。

在实际开发中,如果不需要多线程安全,Python 内置的 collections.deque 是队列的首选实现,它提供了 O(1) 的 append 和 popleft 操作,比用列表手动实现更高效。

八、总结

本文全面介绍了队列这一基础数据结构:

  • 队列遵循先进先出原则,只允许在队尾插入、队头删除。

  • 基本操作:enqueuedequeuefront 等,时间复杂度均为 O(1)。

  • 实现方式有顺序队列(循环队列)链式队列,各有优劣,循环队列适合固定容量场景,链式队列适合动态增长场景。

  • 队列在任务调度、缓冲、广度优先搜索等领域有广泛应用。

  • 了解队列的变体(双端队列、优先队列等)可以应对更复杂的问题。

队列虽简单,却是构建复杂系统和算法的基石。掌握队列,你就掌握了"排队"的智慧,也为深入理解操作系统、网络编程、算法设计铺平了道路。

相关推荐
灰色小旋风2 小时前
力扣18 四数之和(C++)
数据结构·算法·leetcode
噜啦噜啦嘞好2 小时前
算法篇:前缀和
数据结构·算法
郝学胜-神的一滴3 小时前
冷却时间下的任务调度最优解:从原理到实现
数据结构·c++·算法·面试
今儿敲了吗3 小时前
DS-2 有/无头结点的单向链表
数据结构·笔记·链表
啊董dong4 小时前
noi-2026年3月24号作业
数据结构·c++·算法
WolfGang0073214 小时前
代码随想录算法训练营 Day19 | 回溯算法 part01
数据结构·算法
cheems95274 小时前
[数据结构]栈和队列的互相模拟实现
数据结构·算法
计算机安禾4 小时前
【数据结构与算法】第6篇:线性表(二):单链表的实现(头插法、尾插法)
c语言·数据结构·学习·算法·链表·visual studio code·visual studio
左左右右左右摇晃4 小时前
数据结构——链表
数据结构·链表