取模运算使rear和front在数组末尾后回到开头,实现逻辑环形;队空判front==rear,队满判(rear+1)%capacity==front;rearElement需(rear-1+capacity)%capacity防负。

为什么 front 和 rear 要用取模运算
顺序循环队列本质是用一维数组模拟环形结构,物理上数组是线性的,但逻辑上要让 rear 到末尾后回到开头。不用 % size 就会越界——比如数组长度为 5,当前 rear == 4,入队后应变成 0,而不是 5。所有移动操作(rear = (rear + 1) % capacity、front = (front + 1) % capacity)都依赖这个取模,否则就不是“循环”。
如何判断队空和队满(最容易错的两个条件)
顺序循环队列无法靠 front == rear 同时表示空和满,必须牺牲一个存储位置或引入额外变量。主流做法是「牺牲一个位置」:用 (rear + 1) % capacity == front 判满,用 front == rear 判空。这意味着容量为 n 的数组最多存 n-1 个元素。若强行塞满 n 个,front 和 rear 将再次相等,与队空冲突,导致逻辑崩溃。
- 判空:
front == rear - 判满:
(rear + 1) % capacity == front - 入队前必须先判满,出队前必须先判空
- 初始化时
front = rear = 0,此时队空
enqueue 和 dequeue 的标准写法(带边界检查)
这两个操作看似简单,但漏掉判空/判满、忘记更新索引、搞反取模对象,都会引发未定义行为。尤其注意:入队是先赋值再动 rear,出队是先取值再动 front;且所有索引更新必须立即取模,不能等到最后算。
class CircularQueue {
private:
int* data;
int capacity;
int front;
int rear;
public:
CircularQueue(int k) : capacity(k + 1), front(0), rear(0) {
data = new int[capacity];
}
bool enqueue(int value) {
if ((rear + 1) % capacity == front) return false; // 队满
data[rear] = value;
rear = (rear + 1) % capacity;
return true;
}
bool dequeue() {
if (front == rear) return false; // 队空
front = (front + 1) % capacity;
return true;
}
int frontElement() {
if (front == rear) throw std::runtime_error("Queue is empty");
return data[front];
}
int rearElement() {
if (front == rear) throw std::runtime_error("Queue is empty");
return data[(rear - 1 + capacity) % capacity];
}};
立即学习“C++免费学习笔记(深入)”;
为什么 rearElement() 要写成 (rear - 1 + capacity) % capacity
直接写 (rear - 1) % capacity 在 rear == 0 时会得到负数(如 -1),C++ 中负数取模结果仍为负(-1 % 5 == -1),而非预期的 4。加一个 capacity 再取模可强制转正:(0 - 1 + 5) % 5 == 4。这是循环队列里访问“逻辑上 rear 前一个位置”的安全写法,任何涉及减法后取模的地方都要防负。
实际使用中,只要索引计算含减法,就该默认套一层 + capacity 再取模。这不是过度防御,是 C++ 取模语义决定的硬约束。










