注:写本博客的目的接在记录学习过程,学习c++的新知识点,以及巩固以前学过的知识!!!
/*
1.优先级队列的定义
2.仿函数
3.库中的仿函数不满足时,我们可以自己实现仿函数
*/
priority_queue:优先级队列。
1.优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素。
所以可以把priority_queue当作堆来实现以及使用
结构:因为它为容器适配器,底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭 代器访问,并支持以下操作:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
3.标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指 定容器类,则使用vector。
那么结构清晰明了
template<class T,class Container>
class priority_queue{
public:
priority_queue(){}
template<class iterator>
priority_queue(iterator first,iterator last){
while(first!=last){
_con.push_back(*first);
first++;
}
//向下调整--建堆
for(int parent=(_con._size()-1-1)/2;parent>=0;parent--){
adjustDown(parent);
}
}
void push(const T&val){
_con.push_back(val);
adjustUp(_con.size()-1);
}
void pop(){
swap(_con[_con.size()-1],_con[0]);
_con.pop_back();
adjustDown(0);
}
const T& top() const{
return _con.front();
}
const T& top() const{
return _con.front();
}
size_t size() const{
return _con.szie();
}
bool empty() {
return _con.empty();
}
protected:
adjustDown(int parent){
int child=parent*2+1;
while(child<_con.size()){
if(child+1<_con.size()&&_con[child+1]<_con[child]){
child++;
}
if(_con[child]<_con[parent]){
std::swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}else{
break;
}
}
}
//向上调整
void adjustUp(size_t child) {
size_t parent = (child - 1) / 2;
while (child>0) {
if (_con[child]<_con[parent]) {
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}else {
break;
}
}
}
private:
Container _con;
}
那如果我们要实现一个大堆呢?还需要修改代码,是不是很麻烦,所以这里要引入一个新的特性:
仿函数。
特点:
仿函数是C++中的一种数据类型,也叫函数对象。它就像函数一样,可以接收参数并返回结果。但不同之处在于,仿函数不是一个普通的函数,而是一个重载了“()”运算符的对象,因此可以像函数一样调用。
仿函数与函数的区别为:
仿函数是一个类,而函数不是。
仿函数可以有自己的状态,即它们可以有自己的成员变量,而函数不行。
仿函数可以重载 operator(),从而实现多种不同的行为,而函数只能通过不同的函数名或参数列表来实现类似的多态。
仿函数更灵活,可以作为参数传递给更多的函数或算法,而函数只能作为参数传递给支持函数指针的函数。
为了能够支持建立大堆与小堆从而进行升序与降序
template<class T>
struct Less{
bool operator(const T&left,const T&right){
return left<right;
}
};
template<class T>
struct greater {
bool operator()(const T&left,const T&right){
return left > right;
}
};
上面的主代码便可以修改为如下代码
在模板参数里面新增了一个Compare
template<class T,class Container=std::vector<T>,class Compare=less<T>>
class priority_queue {
public:
priority_queue()
{
}
template<class Iterator>
priority_queue(Iterator first,Iterator last) {
while (first!=last) {
_con.push_back(*first);
first++;
}
//构建堆
for (int i = (_con.size() - 1) / 2; i >= 0;i--) {
adjustDown(i);
}
}
void push(const T val) {
_con.push_back(val);
//向上调整
adjustUp(_con.size()-1);
}
void pop() {
std::swap(_con[_con.size()-1],_con[0]);
_con.pop_back();
adjustDown(0);
}
const T& top() const{
return _con.front();
}
size_t size() const{
return _con.szie();
}
bool empty() {
return _con.empty();
}
protected:
void adjustUp(size_t child) {
Compare _compare; //question1
size_t parent = (child - 1) / 2;
while (child>0) {
if (_compare(_con[child] , _con[parent])) {//question2
std::swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else {
break;
}
}
}
void adjustDown(size_t parent) {
Compare _compare;
size_t child = parent * 2 + 1;
while (child<_con.size()) {
if ((child + 1 < _con.size()) && _compare(_con[child+1], _con[child])) {
child++;
}
if (_compare(_con[child] , _con[parent])) {
std::swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else {
break;
}
}
}
private:
Container _con;
};
思考:question1:使用仿函数时,需不需要生成仿函数的对象?
答:还请看qustion2处的调用,如果我们使用匿名函数:Compare()(_con[child],_con[parent]) 使用匿名对象,就不用创建compare对象了。
调用:
下面看这个调用代码:
priority_queue<Data*> q2;
q2.push(new Data(2021,10,1));
q2.push(new Data(2022,10,1));
q2.push(new Data(2023,10,1));
cout<<q2.top()<<ednl;
会发现每次的调用结果都不一样原因是我们的比较代码比较的是Data*这个地址,而比较地址又不是我们想要的,即库里面给出的比较函数不符合我们的预期,我们便可以自己实现比较代码
struct PDataLess{
bool operator(const Data *Left,const Data *right){
return *left<*right;
}
}
调用:priority_queue<Data*,vector<Data*>,PDataLess> q2
我们使用自己的仿函数去比较。