数据结构 - 队列
队列的概念
队列是一种先进先出(First In First Out, 简称 FIFO)的数据结构。
队列的概念及基本术语
队头、队尾、入队、出队:队列(Queue)是一种运算受限的特殊线性表。队列是从表的一端(队尾rear)入队,从表的另一端(队头front)出队。
“先进先出”:假设有队列 $Q=(a_1,a_2,a_3,\dots,a_n)$,则队列 Q 中的元素是按 $a_1,a_2,a_3,\dots,a_n$ 的顺序依次入队,也只能按照 $a_1,a_2,a_3,\dots,a_n$ 的顺序依次出队。因此,队列也被称作先进先出线性表(FIFO-First In First Out)。
在入队、出队的过程中,队列呈现如下几种状态:
队空:队列中没有任何元素;
队满:队列空间已全被占用;
溢出:
当队列已满,却还有元素要“入队”,就出现“上溢(overflow)”;
当队列已空:却还要做“出队”操作,就出现“下溢(underflow)”。
两种情况合在一起,统称为队列的“溢出”,也是 “真溢出”,后文会提到 “假溢出”。
队列的存储
队列可以用数组表示,并设置两个指针:队头指针 front 和队尾指针 rear。
为简化操作,通常约定 front 指向队头元素所在的位置,rear 就指向队尾元素的下一个位置(即 front 就指向队头元素,rear 指向队尾元素的后一个位置)。
队列的操作
定义:
初始化:队头、队尾指针均指向 0。
入队:元素先进入 rear 的位置,然后 rear 再加 1,若 rear == N,表明队列已满。
出队:先取队首(front)元素,再出队(front 加 1),若 front == rear,表明队列已空。
手写队列
#include<iostream>
using namespace std;
const int N=1e6;
struct queue {
int a[N];
int rr=0,ff=0;
void push(int n) { a[rr++]=n; }
void pop() { if(ff<rr) ff++; }
bool empty() { return (ff==rr)?0:1; }
int size() { return rr-ff; }
int front() { return a[ff]; }
int back() { return a[rr-1]; }
} q;
int main() {
q.push(1);//插入元素1
q.push(2);//插入元素2
q.push(114514);//插入元素114514
printf("%d %d %d %d\n",q.empty(),q.front(),q.back(),q.size()); //输出s是否为空、s的顶元素和s的元素个数
q.pop();//删除插入的元素
printf("%d %d %d %d\n",q.empty(),q.front(),q.back(),q.size()); //输出s是否为空、s的顶元素和s的元素个数
q.pop();//删除插入的元素
printf("%d %d %d %d\n",q.empty(),q.front(),q.back(),q.size()); //输出s是否为空、s的顶元素和s的元素个数
q.pop();//删除插入的元素
printf("%d %d\n",q.empty(),q.size()); //再次输出s是否为空和s的素个数
return 0;
}
当我们插入 $9\times 10^5$ 个元素,我们把他们都删掉,再加入 $9\times 10^5$ 个元素,此时队列中只有一个元素,但是此时队满了,如果我们再插入一个元素会发现代码 RE 了,队列中数组访问越界,此时,与刚才的"真溢出"不同,因为队列原本是可以容纳 $9\times 10^5$ 个元素,所以称为 “假溢出”。
我们做一个循环队列优化,避免这个问题。
循环队列
循环队列的实现思路非常简单,当队尾越界数时,使队尾等于 $0$ 即可,注意此时统计队列长度的方式也要稍加改动。
#include<iostream>
using namespace std;
const int N=1e7;
struct queue {
int a[N];
int rr=0,ff=0;
void push(int n) {
a[rr++]=n;
if(rr==N-1) rr=0;
}
void pop() { if(ff!=rr) ff++; }
bool empty() { return (ff==rr)?0:1; }
int size() { return (rr-ff+N)%N; }
int front() { return a[ff]; }
int back() { return a[rr-1]; }
} q;
int main() {
q.push(1);//插入元素1
q.push(2);//插入元素2
q.push(114514);//插入元素114514
printf("%d %d %d %d\n",q.empty(),q.front(),q.back(),q.size()); //输出s是否为空、s的顶元素和s的元素个数
q.pop();//删除插入的元素
printf("%d %d %d %d\n",q.empty(),q.front(),q.back(),q.size()); //输出s是否为空、s的顶元素和s的元素个数
q.pop();//删除插入的元素
printf("%d %d %d %d\n",q.empty(),q.front(),q.back(),q.size()); //输出s是否为空、s的顶元素和s的元素个数
q.pop();//删除插入的元素
printf("%d %d\n",q.empty(),q.size()); //再次输出s是否为空和s的素个数
return 0;
}
2.STL_queue (懒人的法宝)
queue 中的常用操作:
front():返回 queue 中的第一个元素的引用。
back():返回 queue 中最后一个元素的引用。
push():在 queue 的尾部添加一个元素。
pop():删除 queue 中的第一个元素。
size():返回 queue 中元素的个数。
empty():如果 queue 中没有元素,则返回 true。
代码实现:
#include<iostream>
#include<queue> //一个头文件、方便千万行代码
using namespace std;
const int N=1e7;
queue<int> s;//声明了一个int类型的队列
//如果想声明char类型,就写 queue<char> s;
//想声明long long类型,就写 queue<long long> s;
//以此类推
int main() {
s.push(1);//插入元素1
s.push(2);//插入元素2
s.push(114514);//插入元素114514
printf("%d %d %d %d\n",s.empty(),s.front(),s.back(),s.size()); //输出s是否为空、s的顶元素和s的元素个数
s.pop();//删除插入的元素
printf("%d %d %d %d\n",s.empty(),s.front(),s.back(),s.size()); //输出s是否为空、s的顶元素和s的元素个数
s.pop();//删除插入的元素
printf("%d %d %d %d\n",s.empty(),s.front(),s.back(),s.size()); //输出s是否为空、s的顶元素和s的元素个数
s.pop();//删除插入的元素
printf("%d %d\n",s.empty(),s.size()); //再次输出s是否为空和s的素个数
//注意:stl无法返回空队列的front和back元素,如果这样写就会RE
return 0;
}

浙公网安备 33010602011771号