试图进行算法复健.jpg
预警:本文基本复制黏贴oiwiki,仅供个人学习使用
10.28_updated:请勿使用rs函数版本左偏树,在绝大部分编译环境下使用会发生错误
https://oi-wiki.org/ds/leftist-tree/
引入定义:
外节点:没有左儿子或右儿子的节点
空节点:没有左儿子和右儿子的节点
dist:外节点或空节点dist记为1,其他节点为到其子树中最近外节点的距离+1(故每个节点的dist=min{左节点dist,右节点dist}+1)
注意:若一个树的总数为n,则其root的dist最多不超过log[n+1]的下界
左偏树定义
左偏树为一颗二叉树,其满足堆的性质(即父节点大于子节点),且每个节点左儿子的dist都大于等于右儿子的dist,故每个节点的dist都为右节点dist+1(注意一条左链也是左偏树)
左偏树操作
以维护小根堆左偏树为例
merge操作
合并两个堆时,取较小的堆顶节点作为新堆顶,然后将该节点的左儿子保留为新堆根节点的左儿子,取其右儿子与另一个堆重复上述步骤,取合并后的堆作为新堆根节点的右儿子,合并后检查dist值并通过交换儿子维护.
code:
1 | int merge(int x,int y) |
时间复杂度分析:由于每次merge操作,需要合并的两个堆中都会有一个堆根节点的dist-1,故最多执行log次merge操作
可以理解为锁死节点的dist为右节点的dist+1,然后通过每次保留左子树来达到启发式合并的类似效果?
插入操作
将单个节点视作堆,直接用merge就行
删除根节点
合并根节点的左右子树就行
删除任意节点
将欲删除节点的左右子树合并后,向上更新
1 | //使用rs函数,免去交换子树的操作 |
复杂度不变,仍是log级
注意使用rs()函数的代码可能会因为不同编译器的执行顺序出坑
为堆添加标记
1 | void pushdown(int x) |
随机合并
1 | int merge(int x, int y) { |
随机合并,平均复杂度仍为log且省去dist的相关计算
在某些左偏树被卡的情况相当快(比如:https://www.luogu.com.cn/problem/P3273)
配对堆
待补
可持久化可并堆
即在左偏树的merge操作中,每次merge都添加一个新节点
1 | int merge(int x,int y) |
时间复杂度与空间复杂度均为qlogN