左偏树

487 字
2 分钟
左偏树
2024-07-08
浏览量 加载中...

简介#

左偏树是一种可并堆,即可以快速合并的堆。

定义#

对于一棵二叉树,定义 外节点 为子结点小于两个的节点。

定义一个节点的 distdist 为其到子树中最近的外节点所经过的边的数量,外节点的 distdist00

左偏树是一棵二叉树,它不仅具有对的性质,并且每个节点左儿子的 distdist 都大于等于右儿子的 distdist

左偏树每个节点的 distdist 都等于其右儿子的 distdist 加一。

操作#

为了方便,本文操作均讨论大根堆。

合并 merge#

合并是左偏树的的核心操作。

合并两个堆时,由于要满足堆性质,先取值较大的那个根作为合并后堆的根节点。

然后将根的左儿子作为合并后堆的左儿子,递归合并其右儿子与另一个堆,作为合并后的堆的右儿子。

为了满足左偏树的性质,若合并完后左儿子的 distdist 小于右儿子的 distdist,就交换左右儿子。

void merge(int x,int y) {
if(!x||!y) return x+y;//若一个堆为空,返回另一个堆
if(w[x]<w[y]) swap(x,y);
rson[x]=merge(rson[x],y);
if(dist[lson[x]]<dist[rson[x]])
swap(lson[x],rson[x]);
dist[x]=dist[rson[x]]+1;//更新 dist
return x;
}

由于左偏性质,每递归一层,其中一个堆根节点的 distdist 就会减小 11,而一棵有 nn 个节点的二叉树,根的 distdist 不超过 log(n+1)\left\lceil\log (n+1)\right\rceil,所以合并两个大小分别为 nnmm 的堆复杂度是 O(logn+logm)O(\log n+\log m)

插入节点 insert#

单个节点也可以视为一个堆,合并即可。

删除根 pop#

合并根的左右儿子即可。

删除节点 erase#

先将左右儿子合并,然后自底向上更新 distdist,不满足左偏性质时交换左右儿子,当 distdist 无需更新时结束递归。

void pushup(int x) {
if(!x) return;
if(dist[x]!=dist[rson[x]]+1) {
dist[x]=dist[rson[x]]+1
pushup(fa[x]);
}
}
void erase(int x) {
int y=merge(lson[x],rson[x]);
fa[y]=fa[x];
if(lson[fa[x]]==x)
lson[fa[x]]=y;
else rson[fa[x]]=y;
pushup(fa[y]);
}
左偏树
https://dolphin613.netlify.app/posts/leftist-tree/
作者
Dolphin613
发布于
2024-07-08
许可协议
CC BY-NC-SA 4.0
最后更新于 2024-07-08,距今已过 611 天

部分内容可能已过时

评论区

Profile Image of the Author
Dolphin613
某只会打代码的海豚
分类
标签
站点统计
文章
6
分类
2
标签
9
总字数
15,629
运行时长
0
最后活动
0 天前

目录