配对堆
2020年3月24日
简介
配对堆是一种可合并堆,支持一下几种操作(假设讨论小根堆)
find_min | 返回堆中最小元素 | $O(1)$ |
merge | 将两个配对堆合并为 1 个 | $O(1)$ |
insert | 向堆中插入一个元素 | $O(1)$ |
decrease_key | 减小一个元素的值 | $o(\log n)$[^1] |
delete_min | 删除最小值 | $O(\log n)$ |
相比其他可合并堆(左偏树、菲波那契堆),配对堆实现更简单,均摊复杂度优越。
配对堆结构
一个配对堆要么是一个空堆,要么是一个配对树。配对树由一个根元素和可能为空的配对树列表组成。一般选择左孩子右兄弟法存储配对树。
struct Node { int v; Node *son, *sib; };
操作
find-min
直接返回堆顶元素。
merge
直接比较两个堆的堆顶元素,将值较大的作为另一个的儿子。
Node* merge(Node *a, Node *b) { if (!a) return b; if (!b) return a; if (a -> v > b -> v) swap(a, b); b -> sib = a -> son; a -> son = b; return a; }
insert
构造一个只包含需要插入元素的堆,merge
一下。
delete_min
唯一比较复杂的一个操作。
将根的儿子们一对一对地合并,在将合并后的儿子们逐个合并起来。需要注意的是两个过程的方向需要是相反的,这样才能保证操作的复杂度(???)。
Node* delete_min(Node *u) { return merge_pairs(u -> son); }
merge_pairs
实现合并一个节点的所有兄弟
Node* merge_pairs(Node *u) { if (!u || !u -> sib) return u; Node *m = u -> sib, *n = m -> sib; return merge(merge(u, m), merge_pairs(m)); }
参考代码
const int MAXN = 100; struct PairTree { int v = 0; PairTree *son = 0, *sib = 0; PairTree(int x = 0) : v(x) { } }pool[MAXN], *pptr = pool; PairTree* newPairTree(int x) { pptr -> v = x; return pptr++; } struct PairHeap { PairTree *rt; PairHeap() : rt(0) { } PairTree* merge(PairHeap &rhs) { return rt = merge(rt, rhs.rt); } PairTree* merge(PairTree *a, PairTree *b) { if (!a) return b; if (!b) return a; if (a -> v > b -> v) swap(a, b); b -> sib = a -> son; a -> son = b; return a; } PairTree* insert(int x) { auto p = newPairTree(x); return rt = merge(p, rt); } void pop() { auto bu = rt; rt = merge_pairs(rt -> son); delete bu; return; } PairTree* merge_pairs(PairTree *u) { if (!u || !u -> sib) return u; auto m = u -> sib, n = m -> sib; return merge(merge(u, m), merge_pairs(n)); } int top() { return rt -> v; } bool empty() { return !rt; } };
模板题
[^1]:比较玄学,近似$o(\log n)$
Views: 54
上一篇
最小树形图(有向图最小生成树)
下一篇