教练说这个不难,还比较有用,就扔给我们了 awa
Part1. 可并堆
顾名思义,可并堆是一种支持合并操作的堆。它支持:
- 快速查询最小值/最大值(要么是大根堆,要么是小根堆)
- 插入一个元素
- 删除堆顶
- 合并两个堆
下面引入的左偏树是可并堆的一种实现方式,各操作的时间复杂度为:
- 查询最值,即堆顶:$O(1)$;
- 插入元素 $O(\log n)$;
- 删除堆顶 $O(\log n)$;
- 合并堆 $O(\log n)$;
Part2. 左偏树
左偏树是一种满足堆性质的二叉树。(以下均默认为大根堆)
Part2/1. 左偏性质
称一个没有左儿子或右儿子的节点 u 为 “外节点”;反之则为内节点。
对于外节点,定义其“距离”为 $0$;对于内节点,定义它的距离为 在它的子树中,与它最近的外节点的距离。
左偏树满足左偏性质:
对于任意节点,满足左儿子的距离大于等于右儿子的距离(或者右儿子为空)。
举个例子,一个左偏树可能长这样:
但是下面就不是左偏树(节点上的标号是“距离”):
Part2/2. 求距离
因为右儿子的距离一定小于左儿子的距离,所以如果一个节点不是外节点,那么它的距离 dis[u]
其实就是 dis[rch[u]]
。
因此,根节点的距离不会超过 $\left\lfloor \log (n+1)\right\rfloor-1$。
Part2/3. 合并操作
【定义】
key[u]
u的键值;dis[u]
u的距离;
假设合并的两个堆的根分别是 A,B
,不妨设 key[A]>key[B]
,则把 B
插入到 A
的右子树。如果右子树为空,就直接把 B
插入到 A
的右儿子处。
然后发现 A
的左儿子的距离可能比右儿子小了,我们可以交换 A
的左右儿子来维护左偏性质。
最后更新节点距离就好了。(其实这是一个递归的过程)
1 | Merge(A,B) |
上面 Merge()
的返回值是合并后的根节点。
Part2/4. 其他操作
- 插入元素:把单个元素当成一个堆,与原堆合并
- 删除堆顶:合并堆顶的左子树和右子树
Part3. 源代码
参考题目:洛谷 左偏树模板
1 | /*Lucky_Glass*/ |
另外是指针版的,就直接打包成 struct
了:
1 | struct LHEAP{ |
The End
Thanks for reading!
Email: lucky_glass@foxmail.com,欢迎提问~