Lesson12—queue
本篇博客介绍了c++queue的介绍使用以及模拟实现
文章目录
前言
queue的文档:https://cplusplus.com/reference/queue/queue/
翻译:
- 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端
提取元素。 - 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
- 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:
- 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。
提示:以下是本篇文章正文内容,下面案例可供参考
一、queue的成员函数
常用的就这些,和我上一篇的stack一模一样只不过一个是先进后出一个是先进先出
1 queue
就构造一个空队列
2.empty
检查这个队列是不是空的,返回值是一个布尔类型
3.size
返回队列有多少个值
4.front
取队头的数据,这里就和栈不一样,栈是top,这里要特殊记一下
5.back
取队尾的数据
6.push
7.pop
出数据,这里是先进先出
插入一个数据
二、相关题目
二叉树的层序遍历:https://leetcode.cn/problems/binary-tree-level-order-traversal/description/
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution
{
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int>> vv;
queue<TreeNode*> q;
if(root == nullptr)
{
return vv;
}
q.push(root);
int leveSzie = 1;
while(!q.empty())
{
vector<int> v;
while(leveSzie--)
{
TreeNode* front = q.front();
v.push_back(q.front()->val);
q.pop();
if(front->left)
{
q.push(front->left);
}
if(front->right)
{
q.push(front->right);
}
}
vv.push_back(v);
leveSzie = q.size();
}
return vv;
}
};
三、模拟实现
queue是先进先出,需要头删和尾插,但是呢vector并没有头删函数,主要原因是因为vector头删效率太低了,这里用deque来实现
基本框架
#pragma once
#include<bits/stdc++.h>
template<class T,class Container=deque<T>>
class my_queue
{
private:
Container _con;
};
完整代码
#pragma once
#include<bits/stdc++.h>
template<class T,class Container=deque<T>>
class my_queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
const T& back()
{
return _con.back();
}
const T& front()
{
return _con.front();
}
bool empty()
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
private:
Container _con;
};
这里也可以显示实例化给到list,但是给vector就会报错,这里直接避免了低效的使用
四、deque(双端队列)
deque是一种栈和队列的结合体
从功能上来看,它这里提供了下标访问,也提供了任意位置的插入删除,可以说是结合了栈和队列的优点,但也有缺点
可以说从功能上是vector和list的结合体
双端队列的底层差不多就是这样
缺点:
-
deque的下标访问是不够极致的效率差了差不多1倍的性能
即使把deque的数据拷贝给vector让vector排序,然后在拷贝回来还是比deque快
-
头尾删除还好但是如果是插入到中间又是插入到哪里呢又是要挪数据
deque总结:
五、priority_queue(优先级队列)
优先级队列底层使用的并不是双端队列,用的是vector,因为它底层是堆(默认就是大堆)需要频繁的用下标去访问
它的接口和其他几个容器其实差不多,一看就知道干什么
优先级队列的赋值,可以遍历去push
还有可以用迭代器去给他赋值
甚至数组都可以用迭代器区间(连续的物理空间)
默认情况下它是大堆如果要小堆的话就要在初始化的时候去指定
它后面俩个是缺省参数,传入第三个参数的时候第二个也需要传入
模拟实现
#include<bits/stdc++.h>
using namespace std;
template<class T,class Container = vector<T>>
class my_priority_queue
{
public:
void adjust_up(int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (_con[parent] < _con[child])
{
swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void adjust_down(int parent)
{
int child = parent * 2 + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && _con[child ] < _con[child+1])
{
++child;
}
if (_con[parent] < _con[child])
{
swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
template<class InputIterator>
my_priority_queue(InputIterator first,InputIterator last)
{
while (first != last)
{
_con.push_back(*first);
++first;
}
for (int i = ((_con.size() - 1 - 1) / 2); i >= 0; i--)
{
adjust_down(i);
}
}
void push(const T& x)
{
_con.push_back(x);
adjust_up(_con.size()-1);
}
void pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjust_down(0);
}
bool empty()
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
const T& top()
{
return _con.front();
}
private:
Container _con;
};
上面的代码是默认大堆的,如果要把它改成小堆就需要去改源代码的符号,非常的麻烦,c语言的qsort改升序还是降序用的是一个函数指针自己写一个比较方法,非常的麻烦
c++给出了一个新的概念仿函数(函数对象)
仿函数
仿函数其实就是重载了运算符
运算符重载可以直接这样调用
仿函数其实本质就是个对象
仿函数还可以提供参数自由
了解了上面的就可以实现以下功能
class myless
{
public:
bool operator()(const int& x, const int& y)
{
return x < y;
}
};
int main()
{
myless less1;
cout << less1(1, 2);
return 0;
}
就可以直接这样去比较大小,但我们并不只比较整数,所以这里我们有模板然后显示实例化,灵活性非常高
template<class T>
class myless
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
int main()
{
myless<int> less1;
cout << less1(1, 2) << endl;
myless<string> less2;
cout << less2("1111", "2222") << endl;
return 0;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
template<class T>
class myless
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
class mygreater
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
template<class T,class Container = vector<T>,class Comapre = myless<T>>
class my_priority_queue
{
public:
void adjust_up(int child)
{
Comapre cmp;
int parent = (child - 1) / 2;
while (child > 0)
{
if (cmp(_con[parent] ,_con[child]))
{
swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void adjust_down(int parent)
{
Comapre cmp;
int child = parent * 2 + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && cmp(_con[child ] , _con[child+1]))
{
++child;
}
if (cmp(_con[parent] , _con[child]))
{
swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
template<class InputIterator>
my_priority_queue(InputIterator first,InputIterator last)
{
while (first != last)
{
_con.push_back(*first);
++first;
}
for (int i = ((_con.size() - 1 - 1) / 2); i >= 0; i--)
{
adjust_down(i);
}
}
void push(const T& x)
{
_con.push_back(x);
adjust_up(_con.size()-1);
}
void pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjust_down(0);
}
bool empty()
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
const T& top()
{
return _con.front();
}
private:
Container _con;
};
#include"my_priority_queue.h"
void test1()
{
vector<int> v = { 1,4,7,2,5,8,3,6,9 };
priority_queue<int> pq;
for (auto e : v)
{
pq.push(e);
}
while (!pq.empty())
{
cout << pq.top() << ' ';
pq.pop();
}
}
void test2()
{
vector<int> v = { 1,4,7,2,5,8,3,6,9 };
priority_queue<int> pq(v.begin(),v.end());
while (!pq.empty())
{
cout << pq.top() << ' ';
pq.pop();
}
}
void test3()
{
int arr[] = {1,4,7,2,5,8,3,6,9};
priority_queue<int,vector<int>,greater<int>> pq(arr ,arr+sizeof(arr)/sizeof(arr[0]));
while (!pq.empty())
{
cout << pq.top() << ' ';
pq.pop();
}
}
void test4()
{
vector<int> v = { 1,4,7,2,5,8,3,6,9 ,0,16};
my_priority_queue <int,vector<int>,mygreater<int>> pq(v.begin(), v.end());
while (!pq.empty())
{
cout << pq.top() << ' ';
pq.pop();
}
}
int main()
{
test4();
return 0;
}
总结
队列的底层如果用vector需要头删,头删又需要数据的挪动非常的低效,建议用list或者deque来解决,deque做默认容器还是很不错的
仿函数不仅可以比较大小,还可以用来比较类的大小,大大的提高了灵活性,也是弥补了c语言的一个很大的痛点