Queue
队列
队列(Queue)是遵循先进先出(FIFO)规则的线性数据结构,正如同它的名字一样,先进入队列排队的元素会首先离开队列,像在羽毛球桶里的羽毛球一样,先放进去的球会从下面先取出来。
如图所示,删除队首元素的过程叫做出队,将新元素加入队尾的过程叫做入队。
栈的实现
STL中队列的使用
使用标准库中队列前首先应引入头文件**<queue>**,下面将简单介绍队列简单的使用。
#include <queue>
//队列的声明
std::queue<data_type> q1;
std::queue<data_type> q2;
//向队列中添加元素
q1.push(val);
//删除队首元素
q1.pop();
//复制队列
q1 = q2;
//访问队首和队尾元素
q1.front();
q1.back();
//检查队列是否为空与获取元素
q1.empty();
q1.size();
应注意的一点是,pop()没有返回值,需要先使用front()再次使用pop().
基于链表的实现
class Queue{
private:
Node* front;
Node* back;
int quesize;
public:
Queue(){
front = nullptr;
back = nullptr;
quesize = 0;
}
~Queue(){
freeMemoryNode(front);
}
int getSize(){
return quesize;
}
bool isEmpty(){
return quesize == 0;
}
void push(int val){
Node* node = new Node(val);
if(front == nullptr){
front = node;
back = node;
}
else{
back->next = node;
back = node;
}
quesize++;
}
void pop(){
if(isEmpty()) throw std::runtime_error("队列为空");
Node* tmp = front->next;
delete front;
front = tmp;
quesize--;
}
int peek(){
if(isEmpty()) throw std::runtime_error("队列为空");
return back->val;
}
};
基于数组的实现
为能专注于对队列的实现和理解,我们这里使用标准库中的**<vector>**实现。
在数组中删除元素的操作时间复杂度为$O(n)$,如果我们直接删除的话就不等于队列删除元素的操作时间复杂度$O(1)$了,所以我们这里使用front和back作为队列的首尾,每次删除只将front后移一位,而不删除元素。
#include <vector>
class Queue{
private:
std::vector<int> queue;
int front;
int back;
int quesize;
public:
Queue(){
front = -1;
back = -1;
quesize = 0;
}
~Queue(){
//vector自动管理内存
}
int getSize(){
return quesize;
}
bool isEmpty(){
return quesize == 0;
}
void push(int val){
queue.push_back(val);
if(front == -1){
front = 0;
back = 0;
}
else{
back = queue.size()-1;
}
quesize++;
}
void pop(){
if(isEmpty()) throw std::runtime_error("队列为空");
front++;
quesize--;
}
int peek(){
if(isEmpty()) throw std::runtime_error("队列为空");
return queue[front];
}
};
*为什么不使用C风格数组
如果不使用vector,则需要预制数组大小,并且在即将超出数组大小限制时进行扩容操作,或者将back再次指向那些被`删除`元素的下标,但这样依旧会面临扩容操作,这两种操作方式较为复杂此处不再赘述。
队列各操作效率
| 操作名 | 时间复杂度 |
|---|---|
| pop() | $O(1)$ |
| push() | $O(1)$ |
| peek() | $O(1)$ |
队列相关题目
- 二叉树迭代法实现层序遍历 :102. 二叉树的层序遍历 - 力扣(LeetCode)