配对堆
储存
用 儿子-兄弟表示法 储存,一个节点的所有儿子节点形成一个单向链表。每个节点存储值,第一个儿子的指针,下一个兄弟的指针
任意一个儿子的值都不小于父亲
任何一个满足堆性质的树都是一个合法的配对堆
操作
查询最小值
直接返回堆顶,\(O(1)\)
合并(meld)
若有一个为空则直接返回另一个。否则令两个根节点值较小的为新根节点,较大的插入新根节点的儿子节点链表第一个,时间复杂度 \(O(1)\)
一个节点的儿子链表是按插入时间排序的,即最右边的节点最早成为父节点的儿子,最左边的节点最近成为父节点的儿子
插入
视为一个新的配对堆和原堆合并即可,\(O(1)\)
删除最小值
删去根节点后,将儿子们两两配对并合并(这也是名字的由来),再将合并后的若干堆从右往左(从老到新,否则复杂度会失去保证)依次合并起来
定义函数 merges(x) 表示合并一个节点和它的所有兄弟后新的根节点:
- 该树为空或没有下一个兄弟则直接返回
- 否则令 \(y\) 为根节点 \(x\) 的下一个兄弟,令 \(c\) 为 \(y\) 的下一个兄弟
- 拆散链表,即将 \(x\) 和 \(y\) 指向下一个兄弟的指针置空
- 配对并合并 \(x\) 和 \(y\),递归合并 \(c\) 和它的兄弟,并将两个新树配对并合并,返回合并结果
定义函数 delete_min 为删除最小值后新的根节点指针:
merges根节点 \(x\) 的第一个儿子,设新根节点为 \(t\)- 删除根节点
- 返回 \(t\)
减小一个元素的值
需要为每个节点添加一个父亲指针,当有左兄弟时指向左兄弟,否则指向真正的父亲
上述操作的同时需要维护父亲指针
减小 \(x\) 的值后,显然它和以它为根的子树任满足堆性质,但 \(x\) 的父亲和它之间不一定满足
因此把以 \(x\) 为根的子树分离出来,把它和剩余的部分合并
定义函数 decrease_key 为减小给定指针指向元素的值后新的根节点,传入所在堆的根指针,操作节点指针和新值:
- 跟新节点 \(x\) 的值
- 若 \(x\) 为根则直接返回
- 否则剖出子树 \(x\)。当 \(x\) 为其真正父亲的第一个儿子时,跟新父亲的儿子指针;否则将它从儿子节点链表中删去。并把 \(x\) 的兄弟指针和父亲指针置空
- 配对并合并根和 \(x\),返回新的根
时间复杂度
已证明证明 meld 和 delete_min 均为均摊 \(O(\log n)\),decrease-key 操作均摊复杂度下界至少为 \(\Omega(\log\log n)\)
对复杂度上界的估计有:
\(O(1)\) meld
\(O(\log n)\) decrease-key
\(O(2^{2\sqrt{\log\log n}})\) meld 和 decrease-key
前述都是均摊复杂度,不能各取最小,因此不能可持久化

浙公网安备 33010602011771号