CCF-CSP备战NO.7【队列】

一、队列基础知识

1. 什么是队列?

队列是一种**先进先出(FIFO, First In First Out)**​ 的线性数据结构。就像排队买票一样,先来的人先服务,后来的人排在后面。

2. 队列的基本操作

  • push(x):将元素x插入队尾

  • pop():删除队首元素

  • front():获取队首元素

  • back():获取队尾元素

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

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

3. C++中队列的定义和使用

cpp 复制代码
#include <queue>  // 引入队列头文件
using namespace std;

int main() {
    // 定义一个存储整数的队列
    queue<int> q;
    
    // 入队操作
    q.push(10);
    q.push(20);
    q.push(30);
    
    // 访问队首元素
    cout << "队首元素: " << q.front() << endl;  // 输出10
    
    // 访问队尾元素
    cout << "队尾元素: " << q.back() << endl;   // 输出30
    
    // 出队操作
    q.pop();  // 删除队首元素10
    
    // 判断队列是否为空
    if (!q.empty()) {
        cout << "队列不为空" << endl;
    }
    
    // 获取队列大小
    cout << "队列大小: " << q.size() << endl;  // 输出2
    
    return 0;
}

4.与栈的对比

栈的基本操作
操作 说明 时间复杂度
push(x) 将元素x压入栈顶 O(1)
pop() 弹出栈顶元素 O(1)
top() 获取栈顶元素 O(1)
empty() 判断栈是否为空 O(1)
size() 返回栈中元素个数 O(1)
C++中栈的定义和使用
cpp 复制代码
#include <stack>  // 引入栈头文件
using namespace std;

int main() {
    // 定义一个存储整数的栈
    stack<int> st;
    
    // 入栈操作
    st.push(10);
    st.push(20);
    st.push(30);
    
    // 访问栈顶元素
    cout << "栈顶元素: " << st.top() << endl;  // 输出30
    
    // 出栈操作
    st.pop();  // 删除栈顶元素30
    
    // 判断栈是否为空
    if (!st.empty()) {
        cout << "栈不为空" << endl;
    }
    
    // 获取栈的大小
    cout << "栈大小: " << st.size() << endl;  // 输出2
    
    // 遍历栈(注意:遍历会清空栈)
    while (!st.empty()) {
        cout << st.top() << " ";
        st.pop();
    }
    // 输出: 20 10
    
    return 0;
}

二、经典题型

1.栈与队列的互相实现

(1)双栈实现队列:

push、pop、front、back、empty、size六个操作

思想是构造两个栈,最难的是back()

cpp 复制代码
#include <iostream>
#include <stack>
#include <string>
using namespace std;

class MyQueue {
private:
    stack<int> inStack;    // 输入栈
    stack<int> outStack;   // 输出栈
    int backElement;       // 记录队尾元素(用于back()操作)
    
    // 将输入栈元素转移到输出栈
    void transfer() {
        while (!inStack.empty()) {
            outStack.push(inStack.top());
            inStack.pop();
        }
    }
    
public:
    MyQueue() {}
    
    // 1. push:将元素插入队尾
    void push(int x) {
        inStack.push(x);
        backElement = x;  // 更新队尾元素
    }
    
    // 2. pop:删除队首元素并返回
    int pop() {
        if (outStack.empty()) {
            transfer();
        }
        int frontElement = outStack.top();
        outStack.pop();
        return frontElement;
    }
    
    // 3. front:获取队首元素
    int front() {
        if (outStack.empty()) {
            transfer();
        }
        return outStack.top();
    }
    
    // 4. back:获取队尾元素
    int back() {
        return backElement;
    }
    
    // 5. empty:判断队列是否为空
    bool empty() {
        return inStack.empty() && outStack.empty();
    }
    
    // 6. size:返回队列中元素个数
    int size() {
        return inStack.size() + outStack.size();
    }
};

int main() {
    MyQueue q;
    string operation;
    int value;
    
    cout << "用栈实现队列 - 完整6个操作测试" << endl;
    cout << "支持的指令:" << endl;
    cout << "push x  - 将x入队" << endl;
    cout << "pop     - 出队并显示队首元素" << endl;
    cout << "front   - 查看队首元素" << endl;
    cout << "back    - 查看队尾元素" << endl;
    cout << "empty   - 检查队列是否为空" << endl;
    cout << "size    - 查看队列中元素个数" << endl;
    cout << "exit    - 退出程序" << endl;
    cout << "-----------------------------" << endl;
    
    while (true) {
        cout << "请输入指令: ";
        cin >> operation;
        
        if (operation == "push") {
            cin >> value;
            q.push(value);
            cout << value << " 已入队" << endl;
        }
        else if (operation == "pop") {
            if (q.empty()) {
                cout << "队列为空,无法出队!" << endl;
            } else {
                int result = q.pop();
                cout << "出队元素: " << result << endl;
            }
        }
        else if (operation == "front") {
            if (q.empty()) {
                cout << "队列为空!" << endl;
            } else {
                cout << "队首元素: " << q.front() << endl;
            }
        }
        else if (operation == "back") {
            if (q.empty()) {
                cout << "队列为空!" << endl;
            } else {
                cout << "队尾元素: " << q.back() << endl;
            }
        }
        else if (operation == "empty") {
            cout << (q.empty() ? "队列为空" : "队列不为空") << endl;
        }
        else if (operation == "size") {
            cout << "队列中元素个数: " << q.size() << endl;
        }
        else if (operation == "exit") {
            cout << "程序结束" << endl;
            break;
        }
        else {
            cout << "无效指令,请重新输入!" << endl;
        }
    }
    
    return 0;
}
cpp 复制代码
用栈实现队列 - 完整6个操作测试
支持的指令:
push x  - 将x入队
pop     - 出队并显示队首元素
front   - 查看队首元素
back    - 查看队尾元素
empty   - 检查队列是否为空
size    - 查看队列中元素个数
exit    - 退出程序
-----------------------------
请输入指令: push 1
1 已入队
请输入指令: push 2
2 已入队
请输入指令: push 3
3 已入队
请输入指令: front
队首元素: 1
请输入指令: back
队尾元素: 3
请输入指令: size
队列中元素个数: 3
请输入指令: pop
出队元素: 1
请输入指令: front
队首元素: 2
请输入指令: back
队尾元素: 3
请输入指令: size
队列中元素个数: 2
请输入指令: pop
出队元素: 2
请输入指令: pop
出队元素: 3
请输入指令: empty
队列为空
请输入指令: exit
程序结束

(2)单队列实现栈

push、pop、top、empty、size

cpp 复制代码
#include <iostream>
#include <queue>
#include <string>
using namespace std;

class MyStack {
private:
    queue<int> q;
    int topElement;  // 记录栈顶元素
    
public:
    MyStack() {}
    
    // 1. push:将元素压入栈顶
    void push(int x) {
        q.push(x);
        topElement = x;  // 更新栈顶元素
    }
    
    // 2. pop:移除栈顶元素并返回
    int pop() {
        int size = q.size();
        // 将前size-1个元素移到队尾
        for (int i = 0; i < size - 1; i++) {
            int front = q.front();
            q.pop();
            q.push(front);
            // 更新topElement为倒数第二个元素(新的栈顶)
            if (i == size - 2) {
                topElement = front;
            }
        }
        // 此时队首就是要弹出的栈顶元素
        int result = q.front();
        q.pop();
        return result;
    }
    
    // 3. top:获取栈顶元素
    int top() {
        return topElement;
    }
    
    // 4. empty:判断栈是否为空
    bool empty() {
        return q.empty();
    }
    
    // 5. size:返回栈中元素个数
    int size() {
        return q.size();
    }
};

int main() {
    MyStack s;
    string operation;
    int value;
    
    cout << "用队列实现栈 - 完整5个操作测试" << endl;
    cout << "支持的指令:" << endl;
    cout << "push x   - 将x压入栈顶" << endl;
    cout << "pop      - 弹出栈顶元素并显示" << endl;
    cout << "top      - 查看栈顶元素" << endl;
    cout << "empty    - 检查栈是否为空" << endl;
    cout << "size     - 查看栈中元素个数" << endl;
    cout << "exit     - 退出程序" << endl;
    cout << "-----------------------------" << endl;
    
    while (true) {
        cout << "请输入指令: ";
        cin >> operation;
        
        if (operation == "push") {
            cin >> value;
            s.push(value);
            cout << value << " 已压入栈顶" << endl;
        }
        else if (operation == "pop") {
            if (s.empty()) {
                cout << "栈为空,无法弹出!" << endl;
            } else {
                int result = s.pop();
                cout << "弹出元素: " << result << endl;
            }
        }
        else if (operation == "top") {
            if (s.empty()) {
                cout << "栈为空!" << endl;
            } else {
                cout << "栈顶元素: " << s.top() << endl;
            }
        }
        else if (operation == "empty") {
            cout << (s.empty() ? "栈为空" : "栈不为空") << endl;
        }
        else if (operation == "size") {
            cout << "栈中元素个数: " << s.size() << endl;
        }
        else if (operation == "exit") {
            cout << "程序结束" << endl;
            break;
        }
        else {
            cout << "无效指令,请重新输入!" << endl;
        }
    }
    
    return 0;
}
cpp 复制代码
用队列实现栈 - 完整5个操作测试
支持的指令:
push x   - 将x压入栈顶
pop      - 弹出栈顶元素并显示
top      - 查看栈顶元素
empty    - 检查栈是否为空
size     - 查看栈中元素个数
exit     - 退出程序
-----------------------------
请输入指令: push 1
1 已压入栈顶
请输入指令: push 2
2 已压入栈顶
请输入指令: push 3
3 已压入栈顶
请输入指令: top
栈顶元素: 3
请输入指令: size
栈中元素个数: 3
请输入指令: pop
弹出元素: 3
请输入指令: top
栈顶元素: 2
请输入指令: size
栈中元素个数: 2
请输入指令: pop
弹出元素: 2
请输入指令: pop
弹出元素: 1
请输入指令: empty
栈为空
请输入指令: exit
程序结束

(3)双队列实现栈

核心思想

使用两个队列:主队列(mainQueue) ​ 和 辅助队列(auxQueue)

每次push时,采用"新元素先入辅助队列,再把主队列所有元素搬过来"的策略,这样新元素永远在队首,实现了栈的后进先出。

过程分析

假设我们要依次push 1、2、3:

初始状态:

mainQueue: \[\]

auxQueue: \[\]

push(1):

  1. auxQueue入队1 → auxQueue: 1

  2. mainQueue为空,无需搬运

  3. 交换 → mainQueue: 1, auxQueue: \[\]

push(2):

  1. auxQueue入队2 → auxQueue: 2

  2. mainQueue的1搬到auxQueue → auxQueue: 2, 1

  3. 交换 → mainQueue: 2, 1, auxQueue: \[\]

push(3):

  1. auxQueue入队3 → auxQueue: 3

  2. mainQueue的2,1搬到auxQueue → auxQueue: 3, 2, 1

  3. 交换 → mainQueue: 3, 2, 1, auxQueue: \[\]

此时mainQueue队首是3,队尾是1,符合栈的LIFO

完整代码

cpp 复制代码
#include <iostream>
#include <queue>
#include <string>
using namespace std;

class MyStack {
private:
    queue<int> mainQueue;  // 主队列:存储栈中所有元素
    queue<int> auxQueue;   // 辅助队列:临时存放元素
    
public:
    MyStack() {}
    
    // 1. push:将元素压入栈顶
    void push(int x) {
        // 步骤1:新元素先放入辅助队列
        auxQueue.push(x);
        
        // 步骤2:将主队列中的所有元素依次移到辅助队列
        // 这样新元素就在辅助队列的最前面(队首)
        while (!mainQueue.empty()) {
            auxQueue.push(mainQueue.front());
            mainQueue.pop();
        }
        
        // 步骤3:交换两个队列的角色
        // 现在mainQueue中元素顺序为:[新元素, 旧元素...]
        // 即队首是最新入栈的元素,队尾是最早入栈的元素
        swap(mainQueue, auxQueue);
    }
    
    // 2. pop:移除栈顶元素并返回
    int pop() {
        // 主队列的队首就是栈顶元素
        int topElement = mainQueue.front();
        mainQueue.pop();
        return topElement;
    }
    
    // 3. top:获取栈顶元素
    int top() {
        // 主队列的队首就是栈顶元素
        return mainQueue.front();
    }
    
    // 4. empty:判断栈是否为空
    bool empty() {
        return mainQueue.empty();
    }
    
    // 5. size:返回栈中元素个数
    int size() {
        return mainQueue.size();
    }
};

int main() {
    MyStack s;
    string operation;
    int value;
    
    cout << "双队列实现栈 - 详细测试" << endl;
    cout << "支持的指令:" << endl;
    cout << "push x   - 将x压入栈顶" << endl;
    cout << "pop      - 弹出栈顶元素并显示" << endl;
    cout << "top      - 查看栈顶元素" << endl;
    cout << "empty    - 检查栈是否为空" << endl;
    cout << "size     - 查看栈中元素个数" << endl;
    cout << "exit     - 退出程序" << endl;
    cout << "-----------------------------" << endl;
    
    while (true) {
        cout << "请输入指令: ";
        cin >> operation;
        
        if (operation == "push") {
            cin >> value;
            s.push(value);
            cout << value << " 已压入栈顶" << endl;
            
            // 调试信息:显示当前栈的状态
            cout << "当前栈中元素(从栈顶到栈底): ";
            // 注意:这里只是演示,实际不能直接遍历queue
            cout << "(无法直接遍历)" << endl;
        }
        else if (operation == "pop") {
            if (s.empty()) {
                cout << "栈为空,无法弹出!" << endl;
            } else {
                int result = s.pop();
                cout << "弹出元素: " << result << endl;
            }
        }
        else if (operation == "top") {
            if (s.empty()) {
                cout << "栈为空!" << endl;
            } else {
                cout << "栈顶元素: " << s.top() << endl;
            }
        }
        else if (operation == "empty") {
            cout << (s.empty() ? "栈为空" : "栈不为空") << endl;
        }
        else if (operation == "size") {
            cout << "栈中元素个数: " << s.size() << endl;
        }
        else if (operation == "exit") {
            cout << "程序结束" << endl;
            break;
        }
        else {
            cout << "无效指令,请重新输入!" << endl;
        }
    }
    
    return 0;
}

运行示例及详细过程分析

复制代码
双队列实现栈 - 详细测试
支持的指令:
push x   - 将x压入栈顶
pop      - 弹出栈顶元素并显示
top      - 查看栈顶元素
empty    - 检查栈是否为空
size     - 查看栈中元素个数
exit     - 退出程序
-----------------------------
请输入指令: push 1
1 已压入栈顶
// 内部过程:
// auxQueue: [1]
// mainQueue: [] (空)
// 交换后:mainQueue: [1], auxQueue: []

请输入指令: push 2
2 已压入栈顶
// 内部过程:
// auxQueue: [2]
// mainQueue: [1] → 搬到auxQueue → auxQueue: [2, 1]
// 交换后:mainQueue: [2, 1], auxQueue: []

请输入指令: push 3
3 已压入栈顶
// 内部过程:
// auxQueue: [3]
// mainQueue: [2, 1] → 搬到auxQueue → auxQueue: [3, 2, 1]
// 交换后:mainQueue: [3, 2, 1], auxQueue: []

请输入指令: top
栈顶元素: 3
// mainQueue队首是3,正确!

请输入指令: size
栈中元素个数: 3

请输入指令: pop
弹出元素: 3
// mainQueue队首3被弹出,剩下[2, 1]

请输入指令: top
栈顶元素: 2
// 现在队首是2,正确!

请输入指令: pop
弹出元素: 2

请输入指令: pop
弹出元素: 1

请输入指令: empty
栈为空

请输入指令: exit
程序结束

什么不直接用单队列?

双队列的pop()是O(1),单队列的pop()是O(n)

如果你的代码中pop操作比push多,双队列更优

但双队列的push()是O(n),单队列的push()是O(1)

复杂度对比

操作 单队列实现 双队列实现
push O(1) O(n)
pop O(n) O(1)
top O(1) O(1)
empty O(1) O(1)
size O(1) O(1)

选择建议

  • 如果push操作频繁:选择单队列实现

  • 如果pop操作频繁:选择双队列实现

  • CCF-CSP考试:两种都要掌握,根据题目特点选择

2.滑动窗口最大值

题目描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

示例

复制代码
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]

解释:
窗口位置                    最大值
---------------             -----
[1  3  -1] -3  5  3  6  7    3
 1 [3  -1  -3] 5  3  6  7    3
 1  3 [-1  -3  5] 3  6  7    5
 1  3  -1 [-3  5  3] 6  7    5
 1  3  -1  -3 [5  3  6] 7    6
 1  3  -1  -3  5 [3  6  7]   7

代码

cpp 复制代码
#include <iostream>
#include <vector>
#include <deque>
#include <string>
using namespace std;

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> result;
        deque<int> dq;  // 双端队列,存储下标
        
        for (int i = 0; i < nums.size(); i++) {
            // 1. 移除不在窗口内的队首元素
            // 窗口范围:[i-k+1, i]
            if (!dq.empty() && dq.front() < i - k + 1) {
                dq.pop_front();
            }
            
            // 2. 移除队尾所有比当前元素小的元素
            // 因为这些元素不可能再成为最大值
            while (!dq.empty() && nums[dq.back()] < nums[i]) {
                dq.pop_back();
            }
            
            // 3. 将当前元素下标加入队尾
            dq.push_back(i);
            
            // 4. 当窗口形成时(i >= k-1),记录最大值
            if (i >= k - 1) {
                result.push_back(nums[dq.front()]);
            }
        }
        
        return result;
    }
};

int main() {
    Solution solution;
    int n, k;
    
    cin >> n >> k;
 
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    if (k > n) {
        cout << "窗口大小不能超过数组长度!" << endl;
        return 1;
    }

    vector<int> result = solution.maxSlidingWindow(nums, k);

    for (int i = 0; i < result.size(); i++) {
        cout << result[i] << " ";
    }
    
    return 0;
}

变式1:滑动窗口的中位数

cpp 复制代码
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
using namespace std;

// 滑动窗口中位数函数
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
    vector<double> result;
    
    // 大根堆:存储较小的一半元素
    priority_queue<int> maxHeap;
    // 小根堆:存储较大的一半元素
    priority_queue<int, vector<int>, greater<int>> minHeap;
    
    // 延迟删除:记录需要删除的元素
    unordered_map<int, int> delayedDelete;
    
    // 初始化:将前k个元素加入堆
    for (int i = 0; i < k; i++) {
        maxHeap.push(nums[i]);
    }
    for (int i = 0; i < k / 2; i++) {
        minHeap.push(maxHeap.top());
        maxHeap.pop();
    }
    
    // 计算第一个窗口的中位数
    if (k % 2 == 1) {
        result.push_back((double)maxHeap.top());
    } else {
        result.push_back(((double)maxHeap.top() + (double)minHeap.top()) / 2.0);
    }
    
    // 滑动窗口
    for (int i = k; i < nums.size(); i++) {
        // 1. 标记要删除的元素
        int outNum = nums[i - k];  // 离开窗口的元素
        int inNum = nums[i];       // 进入窗口的元素
        
        delayedDelete[outNum]++;
        
        // 2. 调整平衡:确定outNum在哪个堆中
        int balance = 0;
        if (!maxHeap.empty() && outNum <= maxHeap.top()) {
            balance--;  // outNum在大根堆中
        } else {
            balance++;  // outNum在小根堆中
        }
        
        // 3. 将新元素加入合适的堆
        if (!maxHeap.empty() && inNum <= maxHeap.top()) {
            maxHeap.push(inNum);
            balance++;
        } else {
            minHeap.push(inNum);
            balance--;
        }
        
        // 4. 重新平衡两个堆的大小
        if (balance > 0) {
            // 大根堆多了,移一个到小根堆
            minHeap.push(maxHeap.top());
            maxHeap.pop();
            balance--;
        } else if (balance < 0) {
            // 小根堆多了,移一个到大根堆
            maxHeap.push(minHeap.top());
            minHeap.pop();
            balance++;
        }
        
        // 5. 清理堆顶的延迟删除元素
        while (!maxHeap.empty() && delayedDelete[maxHeap.top()] > 0) {
            delayedDelete[maxHeap.top()]--;
            maxHeap.pop();
        }
        while (!minHeap.empty() && delayedDelete[minHeap.top()] > 0) {
            delayedDelete[minHeap.top()]--;
            minHeap.pop();
        }
        
        // 6. 确保大根堆的大小 >= 小根堆的大小
        if (maxHeap.size() < minHeap.size()) {
            maxHeap.push(minHeap.top());
            minHeap.pop();
        }
        
        // 7. 计算当前窗口的中位数
        if (k % 2 == 1) {
            result.push_back((double)maxHeap.top());
        } else {
            result.push_back(((double)maxHeap.top() + (double)minHeap.top()) / 2.0);
        }
    }
    
    return result;
}

int main() {
    int n, k;
    
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    cin >> k;
    
    vector<double> result = medianSlidingWindow(nums, k);
    
    for (int i = 0; i < result.size(); i++) {
        printf("%.1f", result[i]);
        if (i < result.size() - 1) cout << " ";
    }
    cout << endl;
    
    return 0;
}

变式2:二维矩阵中的滑动窗口最大值

cpp 复制代码
#include <iostream>
#include <vector>
#include <deque>
using namespace std;

// 一维滑动窗口最大值(复用之前的函数)
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    vector<int> result;
    deque<int> dq;
    
    for (int i = 0; i < nums.size(); i++) {
        if (!dq.empty() && dq.front() < i - k + 1) {
            dq.pop_front();
        }
        while (!dq.empty() && nums[dq.back()] < nums[i]) {
            dq.pop_back();
        }
        dq.push_back(i);
        if (i >= k - 1) {
            result.push_back(nums[dq.front()]);
        }
    }
    
    return result;
}

// 二维滑动窗口最大值函数
vector<vector<int>> maxSlidingWindow2D(vector<vector<int>>& matrix, int k) {
    int m = matrix.size();
    int n = matrix[0].size();
    
    // 第一步:对每一行做滑动窗口
    vector<vector<int>> rowResult(m, vector<int>(n - k + 1));
    for (int i = 0; i < m; i++) {
        vector<int> rowMax = maxSlidingWindow(matrix[i], k);
        for (int j = 0; j < rowMax.size(); j++) {
            rowResult[i][j] = rowMax[j];
        }
    }
    
    // 第二步:对中间结果的每一列做滑动窗口
    vector<vector<int>> finalResult(m - k + 1, vector<int>(n - k + 1));
    for (int j = 0; j < n - k + 1; j++) {
        // 提取第j列的所有元素
        vector<int> col(m);
        for (int i = 0; i < m; i++) {
            col[i] = rowResult[i][j];
        }
        
        // 对这一列做滑动窗口
        vector<int> colMax = maxSlidingWindow(col, k);
        for (int i = 0; i < colMax.size(); i++) {
            finalResult[i][j] = colMax[i];
        }
    }
    
    return finalResult;
}

int main() {
    int m, n, k;
    
    // 输入矩阵维度
    cin >> m >> n;
    
    // 输入矩阵
    vector<vector<int>> matrix(m, vector<int>(n));
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            cin >> matrix[i][j];
        }
    }
    
    // 输入窗口大小
    cin >> k;
    
    // 调用函数
    vector<vector<int>> result = maxSlidingWindow2D(matrix, k);
    
    // 输出结果
    for (int i = 0; i < result.size(); i++) {
        for (int j = 0; j < result[i].size(); j++) {
            cout << result[i][j];
            if (j < result[i].size() - 1) cout << " ";
        }
        cout << endl;
    }
    
    return 0;
}

3.循环队列

(1)设计循环队列

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

class MyCircularQueue {
private:
    vector<int> data;
    int head;      // 队首指针
    int tail;      // 队尾指针(指向下一个插入位置)
    int size;      // 当前元素个数
    int capacity;  // 队列容量
    
public:
    // 构造函数
    MyCircularQueue(int k) : data(k), head(0), tail(0), size(0), capacity(k) {}
    
    // 1. 入队
    bool enQueue(int value) {
        if (isFull()) return false;
        data[tail] = value;              // 在tail位置插入
        tail = (tail + 1) % capacity;    // tail后移(循环)
        size++;
        return true;
    }
    
    // 2. 出队
    bool deQueue() {
        if (isEmpty()) return false;
        head = (head + 1) % capacity;    // head后移(循环)
        size--;
        return true;
    }
    
    // 3. 获取队首元素
    int Front() {
        if (isEmpty()) return -1;
        return data[head];
    }
    
    // 4. 获取队尾元素
    int Rear() {
        if (isEmpty()) return -1;
        // tail指向的是下一个插入位置,所以队尾是tail的前一个位置
        // 加capacity防止负数
        return data[(tail - 1 + capacity) % capacity];
    }
    
    // 5. 判空
    bool isEmpty() {
        return size == 0;
    }
    
    // 6. 判满
    bool isFull() {
        return size == capacity;
    }
    
    // 7. 获取当前元素个数
    int getSize() {
        return size;
    }
};

int main() {
    int k, m;
    
    // 输入队列容量和操作次数
    cin >> k >> m;
    
    MyCircularQueue q(k);
    
    while (m--) {
        string op;
        cin >> op;
        
        if (op == "en") {
            int x;
            cin >> x;
            cout << (q.enQueue(x) ? "true" : "false") << endl;
        }
        else if (op == "de") {
            cout << (q.deQueue() ? "true" : "false") << endl;
        }
        else if (op == "front") {
            int val = q.Front();
            if (val == -1) cout << "empty" << endl;
            else cout << val << endl;
        }
        else if (op == "rear") {
            int val = q.Rear();
            if (val == -1) cout << "empty" << endl;
            else cout << val << endl;
        }
        else if (op == "empty") {
            cout << (q.isEmpty() ? "true" : "false") << endl;
        }
        else if (op == "full") {
            cout << (q.isFull() ? "true" : "false") << endl;
        }
        else if (op == "size") {
            cout << q.getSize() << endl;
        }
    }
    
    return 0;
}
cpp 复制代码
输入:
5 10
en 1
en 2
en 3
en 4
en 5
en 6
front
rear
de
en 6
front
rear

输出:
true
true
true
true
true
false  ← 队列满了,6入不了队
1      ← 队首是1
5      ← 队尾是5
true   ← 出队成功
true   ← 6入队成功
2      ← 队首变成2
6      ← 队尾是6

(2)变式1:约瑟夫环问题

题目:n个人围成一圈,从第一个人开始报数,数到m的人出列,下一个人再从1开始报数,直到所有人都出列。求最后剩下的人的编号。

复制代码
#include <iostream>
#include <vector>
using namespace std;

int josephus(int n, int m) {
    // 用数组模拟循环队列
    vector<int> people(n);
    for (int i = 0; i < n; i++) people[i] = i + 1;
    
    int index = 0;  // 当前报数的人
    while (people.size() > 1) {
        // 报数到第m个人
        index = (index + m - 1) % people.size();
        // 移除这个人
        people.erase(people.begin() + index);
        // 注意:erase后,index指向的就是下一个人
    }
    
    return people[0];
}

int main() {
    int n, m;
    cin >> n >> m;
    cout << josephus(n, m) << endl;
    return 0;
}

输入输出示例

复制代码
输入:7 3
输出:4
(7个人,数到3出列,最后剩下4号)

击鼓传花问题同上

题目:n个小朋友围成一圈,从第1个开始传花,每传m次淘汰一个人,求最后留下的小朋友编号。

这和约瑟夫环本质上是同一道题。

(3)变式2:数据流中的滑动平均值

题目:给定一个整数数据流和一个大小为k的滑动窗口,计算每个窗口的平均值。

复制代码
#include <iostream>
#include <vector>
using namespace std;

class MovingAverage {
private:
    vector<int> data;  // 循环队列底层数组
    int head, tail, size, capacity;
    int sum;  // 窗口内元素的和
    
public:
    MovingAverage(int k) : data(k), head(0), tail(0), size(0), capacity(k), sum(0) {}
    
    double next(int val) {
        if (isFull()) {
            // 队列满了,移除队首元素
            sum -= data[head];
            head = (head + 1) % capacity;
            size--;
        }
        
        // 加入新元素
        data[tail] = val;
        tail = (tail + 1) % capacity;
        size++;
        sum += val;
        
        return (double)sum / size;
    }
    
    bool isFull() {
        return size == capacity;
    }
};

int main() {
    int k, n;
    cin >> k >> n;
    
    MovingAverage ma(k);
    
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        printf("%.2f\n", ma.next(x));
    }
    
    return 0;
}

输入输出示例

复制代码
输入:
3 5
1 10 3 5 6

输出:
1.00
5.50
4.67
6.00
4.67

解释:
[1] -> 平均=1
[1,10] -> 平均=5.5
[1,10,3] -> 平均=4.67
[10,3,5] -> 平均=6
[3,5,6] -> 平均=4.67

(4)变式3:任务队列(模拟CPU调度)

题目:给定一组任务,每个任务有执行时间,CPU按时间片轮转的方式执行,求所有任务完成的时间。

复制代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

int roundRobin(vector<int>& tasks, int timeSlice) {
    queue<pair<int, int>> q;  // (任务编号, 剩余时间)
    for (int i = 0; i < tasks.size(); i++) {
        q.push({i, tasks[i]});
    }
    
    int currentTime = 0;
    
    while (!q.empty()) {
        auto [id, remain] = q.front();
        q.pop();
        
        if (remain <= timeSlice) {
            // 这个任务可以在这个时间片内完成
            currentTime += remain;
            cout << "任务" << id << "在时间" << currentTime << "完成" << endl;
        } else {
            // 这个任务没做完,继续排队
            currentTime += timeSlice;
            q.push({id, remain - timeSlice});
        }
    }
    
    return currentTime;
}

int main() {
    int n, timeSlice;
    cin >> n >> timeSlice;
    
    vector<int> tasks(n);
    for (int i = 0; i < n; i++) {
        cin >> tasks[i];
    }
    
    int totalTime = roundRobin(tasks, timeSlice);
    cout << "总耗时: " << totalTime << endl;
    
    return 0;
}

输入输出示例

复制代码
输入:
4 2
5 3 8 6

输出:
任务1在时间5完成
任务0在时间10完成
任务3在时间16完成
任务2在时间22完成
总耗时: 22
相关推荐
闪电悠米1 小时前
力扣hot100-48.旋转图像-转置翻转详解
算法·leetcode·职场和发展
满天星83035772 小时前
【算法】最长递增子序列(三种解法)
算法
啦啦啦啦啦zzzz2 小时前
算法:贪心算法
c++·算法·leetcode·贪心算法
清泓y4 小时前
RAG 技术
算法·ai
阿慧今天瘦了嘛4 小时前
计算机组成原理概述:从硬件到软件的桥梁
计算机网络·算法
网站优化(SEO)专家5 小时前
SEO核心算法拆解:网站排名快速提升的武林秘籍!
算法·搜索引擎·网站排名·核心算法
惊讶的猫5 小时前
CLGSI
人工智能·算法·机器学习
ysa0510306 小时前
【板子】二分答案(最大最小?)
c++·笔记·算法·板子
科技之门6 小时前
百公里管网漏损分级定位实战方案2026
前端·人工智能·算法