首先祝大家除夕夜快乐哈!
/
如同栈和队列一样,优先级队列也是一共容器适配器,它的本质其实是堆,一般情况下底层所用的容器默认都是vector,模拟实现它其实非常容易,只需要复用底层容器的成员函数即可。重点在于设计中所涉猎到的仿函数和向上向下调整算法,针对其他的一些细节我放在gitee中供有兴趣的读者参考。
https://gitee.com/chxchenhaixiao/test_c/blob/master/Priority_queue/Priority_queue/priority_queue.h
向上调整算法:
根据数据结构的基本知识,我们知道堆的底层是其实是数组,物理存储空间是连续的,堆的数据插入从尾部插入后,需要不断调正到合适位置来维持堆。
以大根堆为例:
- 获取堆底下标
- 通过计算求出父节点
- 比较大小swap
void adjustup(size_t child){
size_t parent = (child-1) / 2;
while(child > 0){
if(_con[parent] < _con[child]){
std::swap(_con[parent],_con[child]);
child = parent;
parent = (child-1) / 2;
}
else{
break;
}
}
}
向下调整算法:
用于堆的堆顶元素删除后对其做调整,原理和向上调整算法基本一致,但是其中一些越界问题需要注意
以大根堆为例:
- 获取堆顶下标(0)
- 计算求得子节点
- 比较大小swap
void adjustdown(size_t parent){
size_t child = 2 * parent + 1;
while(child < size() ){
if(child+1 < size() && _con[child+1] > _con[child]){
child++;
}
if(_con[parent] < _con[child]){
std::swap(_con[child],_con[parent]);
parent=child;
child=2*parent+1;
}
else{
break;
}
}
}
这样子我们的大根堆优先级队列就差不多完成了,但是问题在于仅仅这样编辑代码的话,实现小根堆就显得非常麻烦了,一是每次手动改头文件中的比较运算符,二是CV一个小根堆优先级队列对象。
这两种方式不是麻烦就是冗余,
故而衍生出了仿函数来解决这类问题。
仿函数:
什么是仿函数呢?往土了讲,就是使用方法类似于函数的自定义对象。
和C语言中的回调函数,绝大多数的仿函数不会单独使用,而是在函数中去调用
_con[parent] < _con[child];
_con[child] < _con[child+1];
欲将上面两个大根堆的比较标识升级为对大根小根堆都通用的形式,只需要设置一个类模板
而设置类模板之前,需要先定义两个对象
struct less(){
bool operator()(T& x,T& y){
return x<y;
}
};
struct greater()(T& x,T& y){
bool operator()(T& x,T& y){
return x>y;
}
};
如此一来就可以把之前写的两句修改为
Compare()(_con[parent],_con[child]);
Compare()(_con[child],_con[child+1]);
//匿名对象
//Compare()是一个匿名less or greater对象
//这里请结合源码阅读
有了仿函数的帮助,我们就只需要在声明优先级队列的时候改变传递的仿函数就可以实现大根堆和小根堆,就显得非常简便了。
//h