顾名思义,队列是一种先进先出的数据结构。队列有两种实现方式,链队列和循环队列。
链队列可以看作是单链表的一种特殊情况,而循环队列则是一种顺序表,使用一组连续的存储单元依次存放队列元素,用两个指针head和tail分别指示队头和队尾元素,当其中一个走当头时,便会回到开始位置,形成循环。
单端队列
队列代码很容易实现,通常用静态数组来实现队列:
const int N = 1e5; //定义队列大小
int q[N],head,tail; //队列数组,队头,队尾指针
q[head++],q[++tail]=data; //弹出队头,向队尾添加元素
STL queue的主要操作如下:
(1)queue<Type>q:定义队列
(2)q.push(item):把item入队
(3)q.front():返回队首元素,但不删除
(4)q.pop():删除队首元素
(5)q.back():返回队尾元素
(6)q.size():返回元素个数
(7)q.empty():检查队列是否为空
手写循环队列:
#include<bits/stdc++.h>
#define N 2000
struct myqueue{
int data[N];
int head,rear;
bool init(){
head = rear = 0;
return true;
}
int size(){
return (rear - head + N) % N;
}
bool empty(){
if(size()) return false;
else return true;
}
bool push(int e){
if((rear + 1) % N == head) return false;
data[rear] = e;
rear = (rear + 1) % N;
return true;
}
bool pop(int &e){
if(head == rear) return false;
e = data[head];
head = (head + 1) % N;
return true;
}
int front(){
return data[head];
}
};
int main()
{
/*
主要代码
*/
return 0;
}
双端队列和单调队列
双端队列
双端队列是一种具有队列和栈性质的数据结构,它能在两端进行插入和删除,而且也只能在两端插入和删除。
我们可以手写双端队列,形式与上文中实现单端队列类似,不再赘述。也可以使用STL deque,他的用法如下:
- de[i]:返回下标为i的元素
- dq.front():返回队头
- dq.back():返回队尾
- dq.pop_back():删除队尾,不返回值
- dq.pop_front():删除队头,不返回值
- dq.push_back():向队尾入队
- dq.push_front():向队头入队
单调队列
单调队列是双端队列的经典应用。单调队列中的元素的单调有序的,且元素在队列中的顺序和原来在序列中的顺序一致,单调队列的队头队尾都能入队和出队。
应用一(滑动窗口):
下面以洛谷p1440 [求m区间内的最小值]为例,了解如何应用单调队列:
#include<bits/stdc++.h>
#define N 3000000
using namespace std;
int a[N],n,m;
deque<int> dq;
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
cout<<0<<endl;
for(int i = 1; i < n; i++) {
while(!dq.empty() && a[dq.back()] > a[i]) dq.pop_back();
dq.push_back(i);
while(!dq.empty() && i - dq.front() >= m) dq.pop_front();
printf("%d\n",a[dq.front()]);
}
return 0;
}
应用二(最大子序和):
单调队列还可以用于DP优化。
优先队列
优先队列的特征是每次让优先级高的(最大或最小的数)先出队列。优先队列并不是简单的线性结构,而是用堆这种复杂结构来实现。由于手动实现比较麻烦,竞赛时一般用STL,有关优先队列的内容在讨论到堆这一数据结构时,我们在详细讲解。