顺序队列是一种基于数组实现的队列结构,其逻辑遵循“先进先出”(FIFO)原则。它通过两个指针——队头指针 Q.front 和队尾指针 Q.rear 来标识当前队列的起始与结束位置。初始时,二者通常都指向数组起始位置(如0)。入队操作在 Q.rear 处插入元素,并将 Q.rear 后移;出队操作从 Q.front 取出元素,并将 Q.front 后移。
由于顺序队列使用固定大小的数组存储,随着入队和出队操作的进行,即使队列中存在空闲位置(前面已出队腾出的空间),当 Q.rear 到达数组末尾时也无法继续入队,造成“假溢出”。为解决这一问题,引入了循环队列的设计。
循环队列将数组视为首尾相连的环形结构,利用取余运算实现指针的循环移动:
- 入队:
Q.rear = (Q.rear + 1) % MAXSIZE - 出队:
Q.front = (Q.front + 1) % MAXSIZE
此时,空队列和满队列都会出现 Q.front == Q.rear 的情况,因此需要特殊处理以区分两者。常见的方法是牺牲一个存储单元,规定当 (Q.rear + 1) % MAXSIZE == Q.front 时认为队列满,从而用该条件区分空与满状态。
这种结构广泛应用于操作系统中的任务调度、网络数据包缓冲、广度优先搜索(BFS)等需要按序处理的场景。
class CircularQueue:
def __init__(self, k):
self.maxsize = k + 1 # 实际分配k+1个空间,牺牲一个
self.queue = [0] * self.maxsize
self.front = 0
self.rear = 0
def is_empty(self):
return self.front == self.rear
def is_full(self):
return (self.rear + 1) % self.maxsize == self.front
def enqueue(self, value):
if self.is_full():
return False
self.rear = (self.rear + 1) % self.maxsize
self.queue[self.rear] = value
return True
def dequeue(self):
if self.is_empty():
return None
self.front = (self.front + 1) % self.maxsize
return self.queue[self.front]
判断循环队列为空或为满是实现循环队列的关键问题,因为队头指针 front 和队尾指针 rear 在空队列和某些情况下的满队列中可能具有相同的相对位置(即 front == rear),因此需要额外的机制来区分。
一、常见判定策略
1. 牺牲一个存储单元法(最常用)
这是教材和实际编码中最常用的策略:预留一个空位,规定队列满时 (rear + 1) % MAXSIZE == front
- 空队列条件:
front == rear - 满队列条件:
(rear + 1) % MAXSIZE == front - 优点:逻辑清晰,判断简单,易于实现。
- 缺点:浪费一个存储空间。
def is_empty(self):
return self.front == self.rear
def is_full(self):
return (self.rear + 1) % self.maxsize == self.front
2. 设置计数器 count 法
引入一个变量 count 记录当前队列中元素个数。
- 空队列条件:
count == 0 - 满队列条件:
count == MAXSIZE - 优点:充分利用所有空间,无需牺牲单元;判空判满更直观。
- 缺点:需额外维护
count变量,每次入队+1,出队-1。
def is_empty(self):
return self.count == 0
def is_full(self):
return self.count == self.maxsize
3. 增设标志位 tag 法
使用一个布尔标志 tag 标记最近操作是入队还是出队,辅助判断:
- 当
front == rear且tag == 0→ 表示刚进行了出队,队列为空; - 当
front == rear且tag == 1→ 表示刚进行了入队,队列为满。 - 优点:不浪费空间,仅用一个标志位。
- 缺点:逻辑稍复杂,需在每次操作后更新
tag。
示例:入队后若导致
rear == front,则置tag = 1;出队后若导致rear == front,则置tag = 0。
4. 使用长度独立数组索引(高级语言封装)
在 Python 等高级语言中,可通过封装动态管理逻辑长度,内部仍用列表模拟,但对外表现为循环队列行为,自动扩容或使用内置结构(如 collections.deque)。
二、总结对比
| 方法 | 是否浪费空间 | 判断难度 | 额外开销 | 适用场景 |
|---|---|---|---|---|
| 牺牲一个单元 | 是(少用1个) | 简单 | 无 | 教学、嵌入式、C语言实现 |
| 计数器 count | 否 | 简单 | 一个整型变量 | 实际工程常用 |
| 标志位 tag | 否 | 中等 | 一个布尔变量 | 资源受限系统 |
| 动态结构模拟 | 否 | 简单 | 较高内存 | 高级语言应用 |
推荐做法
- 学习阶段:推荐“牺牲一个单元”法,便于理解循环逻辑。
- 工程实践:推荐“计数器法”,性能好且空间利用率高。


1598

被折叠的 条评论
为什么被折叠?



