一、环形队列是什么
环形队列是一种使用固定大小的数组、但逻辑上看起来首尾相连的队列结构。
它的最大特点:
- 不会移动元素
- 入队和出队只移动两个指针(front、rear)
- 操作是 O(1)
极适合作为:缓冲区(Buffer)、生产者-消费者队列、高性能网络收发、音视频流处理、线程间通信等。
二、为什么要用环形队列
普通队列基于数组,会遇到一个问题:
出队时 front++,但前面的空间不能再利用 → 必须移动元素或重新分配。
环形队列的设计目的就是:
- 避免数组移动
- 让释放的空间可以再次使用
做到:既高效,又节省空间。
三、核心思想:余数取模 (mod)
假设数组长度为 capacity,队列中使用两个指针:
- front 指向队头(第一个有效元素)
- rear 指向队尾下一个可写入的位置
移动方式:
- rear = (rear + 1) % capacity
- front = (front + 1) % capacity
比如容量为 5:
索引:0 1 2 3 4 (length=5)
若 rear 从 4 再加 1:
(4 + 1) % 5 = 0
存储结构示意图:

四、环形队列的两个重要规则
判断队列是否为空
front == rear
判断队列是否满?
当 rear 的下一个位置是 front 时,队列为满。
(rear + 1) % capacity == front
队列最多容纳 capacity - 1 个元素。
五、核心操作(以浪费一个空间的经典方案为例)
1. 入队(enqueue)
if (满) return false;
buffer[rear] = value;
rear = (rear + 1) % capacity;
2. 出队(dequeue)
if (空) return false;
value = buffer[front];
front = (front + 1) % capacity;
完整示例:
#include <iostream>
using namespace std;
class Queue {
public:
Queue(int size = 10)
: cap_(size)
, front_(0)
, rear_(0)
, size_(0)
{
pQue_ = new int[cap_];
}
~Queue()
{
delete[] pQue_;
pQue_ = nullptr;
}
// 入队
void push(int val)
{
if ((rear_ + 1) % cap_ == front_) {
// 扩容
expand(2 * cap_);
}
pQue_[rear_] = val;
rear_ = (rear_ + 1) % cap_;
size_++;
}
// 出队
void pop()
{
if (front_ == rear_) {
throw "queue is empty!";
}
front_ = (front_ + 1) % cap_;
size_--;
}
// 队头元素
int front() const
{
if (front_ == rear_) {
throw "queue is empty!";
}
return pQue_[front_];
}
// 队尾元素
int back() const
{
if (front_ == rear_) {
throw "queue is empty!";
}
return pQue_[(rear_ - 1 + cap_) % cap_];
}
// 队空
bool empty()
{
return front_ == rear_;
}
// 队列元素个数
int size() const
{
return size_;
#if 0
// 遍历一遍统计队列元素个数 O(n)
int size = 0;
for (int i = front_; i != rear_; i = (i + 1) % cap_) {
size++;
}
return size;
#endif
}
private:
// 扩容接口
void expand(int size)
{
int* p = new int[size];
int i = 0;
int j = front_;
for (; j != rear_; i++, j = (j + 1) % cap_) {
p[i] = pQue_[j];
}
delete pQue_;
pQue_ = p;
cap_ = size;
front_ = 0;
rear_ = i;
}
private:
int* pQue_;
int cap_; // 空间容量
int front_; // 队头
int rear_; // 队尾
int size_; // 队列元素个数
};
int main()
{
int arr[] = { 12, 4, 56, 7, 89, 31, 53, 75 };
Queue que;
for (int v : arr) {
que.push(v);
}
cout << que.front() << endl;
cout << que.back() << endl;
que.push(100);
que.push(200);
que.push(300);
cout << que.front() << endl;
cout << que.back() << endl;
while (!que.empty()) {
cout << que.front() << " " << que.back() << endl;
que.pop();
}
}
输出结果:

六、常见示例
使用两个栈实现一个队列
用两个栈 inStack 和 outStack:
1. 入队(push/enqueue)
所有数据 直接入 inStack:
inStack.push(x);
2. 出队(pop/dequeue)
当需要出队时:
- 如果 outStack 不为空:直接 pop
- 如果 outStack 为空:把 inStack 所有元素倒入 outStack
while (!inStack.empty()) {
outStack.push(inStack.top());
inStack.pop();
}
倒完后:
- outStack.top() 就是队列队头
- outStack.pop() 完成出队
为什么这样能实现队列?
栈是后进先出(LIFO);队列是先进先出(FIFO)
将栈 A 的数据全部倒到栈 B,会把顺序反转:
入队顺序:1, 2, 3
inStack:top → 3,2,1
倒入 outStack:
outStack:top → 1,2,3
此时 outStack 的 top 就是队列的队头。
完整实现:
#include <stack>
#include <stdexcept>
class QueueWithTwoStacks {
public:
void push(int x) {
inStack.push(x);
}
int pop() {
if (empty()) {
throw std::runtime_error("Queue is empty");
}
moveIfNeeded();
int value = outStack.top();
outStack.pop();
return value;
}
int front() { // 等同于队头 peek
if (empty()) {
throw std::runtime_error("Queue is empty");
}
moveIfNeeded();
return outStack.top();
}
bool empty() const {
return inStack.empty() && outStack.empty();
}
private:
std::stack<int> inStack;
std::stack<int> outStack;
void moveIfNeeded() {
if (outStack.empty()) {
while (!inStack.empty()) {
outStack.push(inStack.top());
inStack.pop();
}
}
}
};
使用示例:
QueueWithTwoStacks q;
q.push(10);
q.push(20);
q.push(30);
std::cout << q.pop() << std::endl; // 10
std::cout << q.front() << std::endl; // 20
q.push(40);
std::cout << q.pop() << std::endl; // 20
时间复杂度分析(关键)

关键点:倒数据的过程只会发生在 outStack 为空时
数据被倒过去以后,不会再倒回来,所以总体均摊下来每个元素只被“倒”一次。所以队列所有操作都是均摊 O(1)。
图示(原理可视化)
队列元素顺序:[1, 2, 3]
(1)入队
inStack: top → 3,2,1
outStack: (empty)
(2)第一次出队时,outStack 为空 → 倒数据
inStack: (empty)
outStack: top → 1,2,3
(3)后续的出队直接从 outStack 弹出
用两个栈实现一个队列:
- 入队:入 inStack
- 出队:如果 outStack 为空,则把 inStack 全倒进去
- 队头在 outStack.top()
- 所有操作均摊 O(1)
使用两个队列实现一个栈
核心思想:
- 栈(stack)是:后进先出(LIFO)
- 队列(queue)是:先进先出(FIFO)
要用两个队列实现栈,就必须想办法让“最后入的元素”成为队列的“队头”。
方法一:push O(n),pop O(1)(最常见的方案)
通过让最新入栈的元素始终保持在队列队头来实现栈。只使用一个主队列 q1,另一个 q2 用于辅助倒数据。
核心思想
push(x):
- 先把 x 放入 q2
- 把 q1 中所有元素搬到 q2 中(保持顺序反转)
- 交换 q1 和 q2
此时:
q1 的 front 就是最新压入的元素(top)
pop:q1.front() 就是栈顶元素,直接 pop 即可。
时间复杂度

方法一:C++ 完整实现
#include <queue>
#include <stdexcept>
class StackWithTwoQueues {
public:
void push(int x) {
q2.push(x);
// 把 q1 所有元素倒进 q2
while (!q1.empty()) {
q2.push(q1.front());
q1.pop();
}
// 交换 q1 和 q2,使 q1 始终为主队列
std::swap(q1, q2);
}
int pop() {
if (q1.empty()) {
throw std::runtime_error("Stack is empty");
}
int value = q1.front();
q1.pop();
return value;
}
int top() {
if (q1.empty()) {
throw std::runtime_error("Stack is empty");
}
return q1.front();
}
bool empty() const {
return q1.empty();
}
private:
std::queue<int> q1, q2;
};
图示理解
假设依次 push:1、2、3
栈顶应该是 3。
push(1)
q1: [1]
push(2)
q2: [2]
q1: [1] → 倒入 q2 → q2: [2,1]
swap(q1, q2)
q1: [2,1]
push(3)
q2: [3]
q1: [2,1] → q2: [3,2,1]
swap → q1: [3,2,1]
此时:
栈顶 = q1.front() = 3
pop 时直接 q1.pop() 即可,非常快。
方法二:push O(1),pop O(n)
思路反过来:
- push 直接入队到 q1
- pop 时为了拿到最后入队的元素,需要将前 n-1 个元素转移到 q2
pop 步骤:
- 把 q1 的前 n-1 个元素全部转移到 q2
- 剩下的就是栈顶元素 → pop 掉
- swap(q1, q2)
时间复杂度

方法二 C++ 实现
class StackWithTwoQueues {
public:
void push(int x) {
q1.push(x);
}
int pop() {
if (q1.empty()) {
throw std::runtime_error("Stack is empty");
}
// 留下最后一个
while (q1.size() > 1) {
q2.push(q1.front());
q1.pop();
}
int value = q1.front();
q1.pop();
std::swap(q1, q2);
return value;
}
int top() {
if (q1.empty()) {
throw std::runtime_error("Stack is empty");
}
// 和 pop 类似,但不删除最后一个
while (q1.size() > 1) {
q2.push(q1.front());
q1.pop();
}
int value = q1.front();
q2.push(value); // 回填
q1.pop();
std::swap(q1, q2);
return value;
}
bool empty() const {
return q1.empty();
}
private:
std::queue<int> q1, q2;
};
对比:两种方案差别

540



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



