算法竞赛中的队列
队列
- 一.队列
- 1.基本概念
- 1.1 核心特性:
- 2.模拟实现
- 二.习题:【模板】队列
- 1.题目
- 2.解题思路
- 2.1 数据结构选择
- 2.2 操作实现
- 2.3 流程控制
- 3.参考代码
- 三.习题:机器翻译
- 1.题目
- 2.解题思路
- 2.1 解题关键字
- 2.2解题思路
- 3.参考代码
- 四.习题:海港
- 1.题目
- 2.解题思路
- 2.1 题眼
- 2.2 解题思路
- 3.参考代码
Hello,小伙伴们!又到了咱们一起捣鼓代码的时间啦!💪 把生活调成热情模式,带着满满的能量钻进编程的奇妙世界吧——今天也要写出超酷的代码,冲鸭!🚀
我的博客主页:喜欢吃燃面
我的专栏:《C语言》,《C语言之数据结构》,《C++》,《Linux学习笔记》
感谢你点开这篇博客呀!真心希望这些内容能给你带来实实在在的帮助~ 如果你有任何想法或疑问,非常欢迎一起交流探讨,咱们互相学习、共同进步,在编程路上结伴成长呀!
一.队列
1.基本概念
在C++中,队列(Queue) 是一种遵循 “先进先出(FIFO,First-In-First-Out)” 原则的线性数据结构,即第一个进入队列的元素会第一个被取出,类似现实中的排队场景。
1.1 核心特性:
-
操作受限:仅允许在 队尾(rear) 插入元素(入队),在 队头(front) 移除元素(出队),不支持随机访问中间元素。
-
常用操作:
push(x):在队尾插入元素x。pop():移除队头元素(无返回值)。front():返回队头元素(不删除)。back():返回队尾元素(不删除)。empty():判断队列是否为空(空返回true)。size():返回队列中元素的个数。
-
实现方式:
- C++标准库中提供
std::queue(定义在<queue>头文件中),底层通常基于双向队列(deque)或链表实现,无需手动管理内存。 - 也可手动用数组或链表实现简单队列(如固定大小的循环队列)。
- C++标准库中提供
-
适用场景:
- 处理按顺序执行的任务(如任务调度、广度优先搜索BFS)。
- 缓冲场景(如打印机任务队列、消息队列)。
如果想更具体了解队列的相关内容:数据结构之队列
2.模拟实现
#include<iostream>
using namespace std;// 定义队列最大容量(1000)
const int N = 1e3;
// 队列存储数组,h为队头指针,t为队尾指针(初始均为0)
int q[N], h, t;// 入队操作:队尾指针后移一位并存入元素
void push(int x) { q[++t] = x; }// 出队操作:队头指针后移一位(逻辑删除队头元素)
void pop() { ++h; }// 获取队头元素:返回当前队头指针向后移动一位指向的元素
int front() { return q[h+1]; }// 获取队尾元素:返回当前队尾指针指向的元素
int back() { return q[t]; }// 获取队列大小:队尾与队头指针的差值即为元素个数
int size() { return t - h; }// 判断队列是否为空:队头与队尾指针相等时为空
bool empty() { return h == t; }int main() {// 测试入队、出队、判空、大小、队头队尾push(1);push(2);push(3);cout << "队列大小: " << size() << endl;cout << "队头元素: " << front() << endl;cout << "队尾元素: " << back() << endl;pop();cout << "出队后队头元素: " << front() << endl;cout << "队列是否为空: " << (empty() ? "是" : "否") << endl;pop();pop();cout << "队列是否为空: " << (empty() ? "是" : "否") << endl;return 0;
}
二.习题:【模板】队列
1.题目
【模板】队列



2.解题思路
要解决这个队列实现问题,我们可以采用数组模拟队列的思路,通过两个指针 h(队头)和 t(队尾)来维护队列的状态。
2.1 数据结构选择
使用数组 q 存储队列元素,h 记录队头的前一个位置(初始为0),t 记录队尾位置(初始为0)。当 h == t 时,队列为空。
2.2 操作实现
- push(x)(入队):将元素
x存入队尾,即q[++t] = x,队尾指针t后移。 - pop()(出队):若队列为空(
h == t),输出ERR_CANNOT_POP;否则队头指针h后移(逻辑上删除队头元素)。 - query()(查询队头):若队列为空(
h == t),输出ERR_CANNOT_QUERY;否则输出q[h+1](队头元素在h+1位置)。 - size()(查询队列长度):队列长度为
t - h。
2.3 流程控制
读取操作次数 n,然后循环 n 次处理每个操作:根据输入的操作类型(1、2、3、4),分别执行入队、出队、查询队头、查询长度的逻辑。
这种数组模拟的方式简单高效,能在 O(1)O(1)O(1) 时间复杂度内完成所有队列操作,适用于本题的需求。
3.参考代码
#include<iostream>
using namespace std;const int N = 1e4 + 10; // 定义队列最大容量
int q[N]; // 用数组存储队列元素
int h, t; // h为队头索引,t为队尾索引(初始均为0)int main()
{int n;cin >> n; // 输入操作次数while (n--) // 循环处理n次操作{int op;cin >> op; // 输入操作类型if (op == 1) { // 操作1:入队int x;cin >> x;q[++t] = x; // 队尾指针后移并存入元素}else if (op == 2) { // 操作2:出队if (t == h) // 队空时无法出队cout << "ERR_CANNOT_POP" << endl;else++h; // 队头指针后移(逻辑删除)}else if (op == 3) { // 操作3:查询队头元素if (t == h) // 队空时无法查询cout << "ERR_CANNOT_QUERY" << endl;elsecout << q[h + 1] << endl; // 输出队头元素}else // 操作4:查询队列长度cout << t - h << endl; // 长度为队尾与队头的差值}return 0;
}
三.习题:机器翻译
1.题目
机器翻译



2.解题思路
2.1 解题关键字
- 内存容量(M)
- 文章长度(N)
- 英文单词(非负整数表示)
- 内存查找
- 外存词典查找(计数目标)
- 先进先出(FIFO)缓存淘汰
- 队列(模拟内存缓存)
2.2解题思路
这道题本质是模拟缓存的FIFO(先进先出)淘汰机制,用队列来模拟内存缓存,统计需要从外存词典查找的次数(即新单词入队的次数)。
-
数据结构选择:
用数组q[M]结合队头指针h、队尾指针t实现队列,模拟内存缓存。队列的长度t - h表示当前内存中存储的单词数。 -
核心逻辑:
- 对于每个输入的单词
x,先检查是否在当前内存(队列)中(通过find函数遍历队列)。 - 若不在内存中:
- 若内存未满(
t - h < M),直接将x入队,外存查找次数cnt加1。 - 若内存已满(
t - h == M),先将队头元素出队(淘汰最早进入的单词),再将x入队,外存查找次数cnt加1。
- 若内存未满(
- 若在内存中,无需操作,继续处理下一个单词。
- 对于每个输入的单词
-
初始化与边界处理:
初始时队列空(h = t = 0),确保每次处理单词时队列状态正确。通过empty函数判断队列是否为空(虽然本题逻辑中可由t - h替代,但保持函数封装性更清晰)。
这种思路通过队列精准模拟了题目中“内存满则淘汰最早进入的单词”的规则,最终 cnt 即为需要外存查找词典的次数。
3.参考代码
#include<iostream>
using namespace std;const int M = 110;int q[M], t, h; // t:队尾指针, h:队头指针(初始均为0,队列为空时即h==t)// 出队:队头指针后移
void pop() { ++h; }// 入队:队尾指针后移并赋值
void push(const int& x) { q[++t] = x; }// 查找元素是否在队列中
bool find(const int& x) {// 从队头下一个元素遍历到队尾for (int i = h + 1; i <= t; ++i) {if (q[i] == x) return true;}return false;
}// 判断队列是否为空
bool empty() { return h == t; }int main() {int m, n, cnt = 0;cin >> m >> n;while (n--) {int x;cin >> x;bool exists = find(x); // 提前判断元素是否存在,减少重复计算if (!exists) { // 元素不存在时才需要处理入队if (t - h < m) { // 队列未满push(x);} else { // 队列已满,先出队再入队pop();push(x);}cnt++; // 只有新元素入队时计数}// 元素已存在则不做操作(隐含continue)}cout << cnt << endl;return 0;
}
四.习题:海港
1.题目
海港




2.解题思路
2.1 题眼
- 时间窗口统计:核心是维护“最近86400秒(24小时)”的时间窗口,统计该窗口内出现的不同元素(如国籍、ID等)的种类数。
- 队列+计数数组:用队列存储元素的“出现时间”和“值”,保证元素按时间顺序排列;用计数数组记录每个元素在当前窗口内的出现次数,辅助统计种类数。
- 动态清理过期元素:每次新增元素后,需循环移除窗口外的旧元素(时间差≥86400秒),并同步更新计数和种类数,确保统计结果实时准确。
2.2 解题思路
-
数据结构选择:
- 队列(
queue<pair<int,int>>):按时间顺序存储元素的“出现时间”和“值”,便于快速获取最早出现的元素(队首),判断是否过期。 - 计数数组(
cnt[N]):记录每个元素在当前窗口内的出现次数,避免重复统计种类。 - 种类计数器(
kinds):实时记录窗口内不同元素的总种类数,简化输出。
- 队列(
-
核心流程:
- 输入处理:每次读取一批元素(如某时间到达的多个乘客),将其“时间+值”存入队列,并更新计数数组(若元素首次出现,
kinds加1)。 - 清理过期元素:以当前时间为基准,循环检查队首元素是否超出24小时窗口(
当前时间 - 队首时间 ≥ 86400)。若过期,弹出队首并减少对应元素的计数,若计数减为0,kinds减1(该元素已完全离开窗口)。 - 输出结果:每次处理完一批元素后,输出当前窗口内的种类数
kinds。
- 输入处理:每次读取一批元素(如某时间到达的多个乘客),将其“时间+值”存入队列,并更新计数数组(若元素首次出现,
-
关键逻辑:
- 队列保证元素按时间有序,只需从队首开始清理过期元素(无需检查中间元素)。
- 计数数组避免因元素重复出现而误减种类数(仅当元素计数从1减为0时,才减少
kinds)。
3.参考代码
#include<iostream>
#include<queue>
using namespace std;typedef pair<int,int> PII; // 定义pair类型,存储(到达时间,国籍)
const int N=1e5+10; // 定义数组大小常量
int cnt[N]; // 统计每个国籍的出现次数
int kinds; // 统计不同国籍的种类数
queue<PII> q; // 队列,存储所有在时间窗口内的(时间,国籍)对int main()
{int n;cin>>n; // 输入船只数量nwhile(n--) // 处理每一艘船的信息{int t,k;cin>>t>>k; // 输入当前船的到达时间t和乘客数量kfor(int i=0;i<k;i++){int x;cin>>x; // 输入每个乘客的国籍xq.push({t,x}); // 将(到达时间,国籍)加入队列if(cnt[x]++==0) // 若该国籍是首次出现,种类数kinds加1kinds++;}// 循环清理超出24小时(86400秒)时间窗口的国籍记录while(!q.empty() && t-q.front().first>=86400){PII tmp=q.front();q.pop(); // 取出并弹出队首元素int x=tmp.second; // 获取该元素的国籍if(cnt[x]--==1) // 若该国籍计数减为0,种类数kinds减1kinds--;}cout<<kinds<<endl; // 输出当前时间窗口内的不同国籍种类数}return 0;
}

