一、队列基础知识
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):
auxQueue入队1 → auxQueue: 1
mainQueue为空,无需搬运
交换 → mainQueue: 1, auxQueue: \[\]
push(2):
auxQueue入队2 → auxQueue: 2
mainQueue的1搬到auxQueue → auxQueue: 2, 1
交换 → mainQueue: 2, 1, auxQueue: \[\]
push(3):
auxQueue入队3 → auxQueue: 3
mainQueue的2,1搬到auxQueue → auxQueue: 3, 2, 1
交换 → 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