由于博主很弱,只会打板子,请见谅
配对堆
一种极其好写又极其快速的堆
先看复杂度
空间复杂度: Θ ( n ) \Theta(n) Θ(n)
时间复杂度:
插入: Θ ( 1 ) \Theta(1) Θ(1)
合并: Θ ( 1 ) \Theta(1) Θ(1)
查询最值: Θ ( 1 ) \Theta(1) Θ(1)
删除元素: O ( l o g   n ) O(log\,n) O(logn)(均摊)
修改元素:下界 Ω ( l o g   l o g   n ) \Omega(log\,log\,n) Ω(loglogn),上界 O ( 2 2 l o g   l o g   n ) O(2^{2\sqrt{log\,log\,n}}) O(22loglogn)(均摊)?反正就是 O ( O( O(玄学 ) ) )
在进操作之前,先看看配对堆的结构
强行模仿STL源码码风警告
不熟练的面向对象及封装警告
配对堆是一种堆有序多叉树。根据要完成的操作,可以给出对该类的定义
template<typename _Tp,typename _Cmp=std::less<_Tp> >
class pairing_heap:private _Cmp
{
typedef _Tp value_type;
typedef _Cmp compare;
private:
struct Node;//单个元素结点
Node* _root;//根指针
size_t s;//记录堆的大小
Node* merge(Node*,Node*);//合并两个堆的内部实现
Node* __pop();//删除函数的内部实现
public:
struct iterator;//迭代器
iterator push(const _Tp&);//在堆中压入新元素
_Tp top()const;//取堆顶
iterator pop();//删除堆顶元素
void join(pairing_heap&);//合并两个堆
bool modify(const iterator&,const _Tp&);//将某位置的值修改
size_t size();//不做解释
bool empty();
};
对每个结点,需维护其父亲及所有的儿子。为了方便在修改元素时将结点分离出来,这里采用双向链表来维护其儿子。具体地讲,父亲的child指针指向第一个儿子,同时每个结点又带有指向右兄弟的指针域。每个结点还有一个“左结点”:对于有左兄弟的结点即为左兄弟,否则为父结点。这种存储方法被称为“左儿子右兄弟”(似乎广义表就是这么存的?)。
来看看图
这是原来的堆
这是堆的实际存储方式
还是很好理解的QWQ
结点的结构体
template<typename _Tp,typename _Cmp>
struct
pairing_heap<_Tp,_Cmp>::Node
{
_Tp value;
Node *left_node,*child,*sibling;
//left_node即“左结点”,child指向最左侧的儿子,sibling指向右兄弟
Node(const _Tp& val=_Tp()):
value(val),
left_node(NULL),
child(NULL),
sibling(NULL) {
}
};
为了修改权值,再写出指向元素的迭代器
template<typename _Tp,typename _Cmp>
struct
pairing_heap