Python数据结构——循环队列

一句话:循环队列是用固定长度数组模拟环形、靠取模绕回解决「假溢出」的队列。物理上还是一段连续数组,逻辑上首尾相接成了环。

一、为什么需要循环队列

普通队列用数组实现时,出队只动 front 指针,元素不断从队头离开,前面就空出一堆位置。但 rear 指针一路向右,等到它撞到数组末尾时,哪怕前面空着,也插不进新元素了------这就是假溢出(false overflow)

现象 真相
数组前面明明有空位,enqueue 却报「满」 rear 到了物理末尾,没绕回去
反复出队后空间「看似」浪费 front 只增不减,没有回收

循环队列的解法:把数组首尾相接想成环rear/front 走到末尾时取模绕回开头。这样「前面空出来的位置」能被重新利用,假溢出消失。

二、核心思路:两个指针 + 取模

只靠 frontrear 两个下标不够------当 front == rear 时,你分不清是「空」还是「满」。两种解法:

  1. 加一个 size 计数器(推荐,直观,不浪费空间)。
  2. 牺牲一个空位:让 (rear + 1) % capacity == front 表示满(容量为 N 只能装 N-1)。

指针前进都靠取模,这是「循环」二字的核心:

python 复制代码
rear = (rear + 1) % capacity   # 入队后前进,到末尾自动绕回开头
front = (front + 1) % capacity # 出队同理

三、完整实现(带 size 计数器,推荐版)

# D:\博客\circular_queue.py(节选:CircularQueue 类)

python 复制代码
# D:\博客\circular_queue.py
class CircularQueue:
    def __init__(self, capacity: int):
        if capacity <= 0:
            raise ValueError("容量必须大于 0")
        self.capacity = capacity
        self.queue = [None] * capacity
        self.front = 0   # 队头下标(出队位置)
        self.rear = 0    # 队尾下标(下一个入队位置)
        self.size = 0    # 当前元素个数

    def is_empty(self) -> bool:
        return self.size == 0

    def is_full(self) -> bool:
        return self.size == self.capacity

    def enqueue(self, item) -> None:
        if self.is_full():
            raise OverflowError("队列已满,无法入队")
        self.queue[self.rear] = item
        self.rear = (self.rear + 1) % self.capacity
        self.size += 1

    def dequeue(self):
        if self.is_empty():
            raise IndexError("队列为空,无法出队")
        item = self.queue[self.front]
        self.queue[self.front] = None
        self.front = (self.front + 1) % self.capacity
        self.size -= 1
        return item

    def peek(self):
        if self.is_empty():
            return None
        return self.queue[self.front]

    def __len__(self):
        return self.size

    def __repr__(self):
        items = []
        i = self.front
        for _ in range(self.size):
            items.append(self.queue[i])
            i = (i + 1) % self.capacity
        return f"CircularQueue({items})"

__repr__ 里用 i = (i + 1) % capacity 按逻辑顺序遍历,保证打印结果和「入队顺序」一致,调试时一眼能看懂。

小结:enqueue 放 rear、动 rear;dequeue 取 front、动 front;两者都取模绕回。判空判满交给 size,干净无歧义。

四、另一种写法:牺牲一个空位判满

不想维护 size 时,用「永远留一个空槽」来区分空和满:

python 复制代码
class CircularQueueWasteOne:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.queue = [None] * capacity
        self.front = 0
        self.rear = 0

    def is_empty(self) -> bool:
        return self.front == self.rear

    def is_full(self) -> bool:
        return (self.rear + 1) % self.capacity == self.front

    def enqueue(self, item) -> None:
        if self.is_full():
            raise OverflowError("队列已满,无法入队")
        self.queue[self.rear] = item
        self.rear = (self.rear + 1) % self.capacity

    def dequeue(self):
        if self.is_empty():
            raise IndexError("队列为空,无法出队")
        item = self.queue[self.front]
        self.queue[self.front] = None
        self.front = (self.front + 1) % self.capacity
        return item

代价:N 容量的数组只能装 N-1 个元素。新手直接用第三节的 size 版,少踩坑。

五、和 collections.deque 的关系

Python 标准库的 deque(双端队列)底层就是类似环形缓冲,两端增删都是 O(1)。deque(maxlen=N) 更是开箱即用的有界循环队列 ------满了再 append 自动丢掉最老的那头,正好对应我们手写的「循环覆盖」语义。

python 复制代码
# D:\博客\circular_queue.py
from collections import deque
dq = deque(maxlen=3)   # maxlen 满了自动丢最老的,自带「循环」语义
for x in range(5):
    dq.append(x)
print(dq)              # deque([2, 3, 4], maxlen=3)

deque 常用 API:

方法 作用
append(x) / appendleft(x) 右端 / 左端入队
pop() / popleft() 右端 / 左端出队
rotate(n) 整体循环平移 n 位
maxlen 构造参数,定长环形

六、什么时候用

  • 手写循环队列:面试手写、理解底层、或特定嵌入式/缓冲场景需要完全掌控。
  • 直接 deque:工程里 99% 的情况。滑动窗口、最近 N 条记录、BFS、栈/队列通吃。
  • 一句话 :凡是频繁从一头或两头增删,别用 list(头部操作 O(n)),用 deque;要练手或面试才手写循环队列。

总结:循环队列 = 数组 + front/rear 取模绕回 + size 判空满,专治普通队列的假溢出。真实项目里 collections.deque(maxlen=N) 一句话顶替它的全部功能。手写一遍是为了把「为什么这么设计」刻进脑子,不是让你上线造轮子。

相关推荐
gb42152871 小时前
python中pypdf库和langchain-unstructured库在解析pdf文件的时候的区别?
python·langchain·pdf
weixin199701080161 小时前
☁️《抖店API基础¥0.018/百次·增值¥0.05/百次:云内云外价差架构实战》(附Python源码)
开发语言·python·架构
磊灬泽1 小时前
【从零学习 MQTT】发布/订阅、主题、QoS 与报文解析
学习
天国梦2 小时前
2026高中生英语词汇学习APP测评:天学网等4款产品实测对比
学习
萌动的小火苗2 小时前
1、python基础面试题
java·开发语言·python
我爱cope2 小时前
【计算机网络 | 物理层3:数字编码与调制:比特如何变成可传输的信号?】
网络·学习·计算机网络
sunywz2 小时前
【从零搭建物联网智能充电桩系统】2、自定义二进制协议:设备为什么不用 JSON?
python·物联网·json
小叮当爱咖啡2 小时前
Day3.参数+Prompt三板斧
开发语言·python·prompt
菜冻鱼3 小时前
Python-pandas-索引与筛选
开发语言·笔记·python·numpy·pandas·学习方法