左偏树(可并堆)学习笔记

引入:堆是一种很常用的数据结构,可以在 Olog(n)的复杂度内进行插入,以维护最大/最小值。但是,如果我们将两个堆合并,即使是启发式合并,时间复杂度也高达 O(sizea×log(sizeb)),而这是我们所不想看到的。那么,有没有一种更好的解决方案呢?

下面来引入一种新的数据结构——左偏树。顾名思义,这是一颗树,但是向左偏。首先定义一个 disu,表示 u 到最近的非满节点(即没有左儿子或右儿子的节点)的距离。

而左偏的性质,若设左儿子右儿子分别为 lsrs,则在这棵树上,对于每个结点,都有 dislsdisrs。显然,当 dis 为定值时,如果令这棵子树结点最多,那么这棵子树是一棵满二叉树。所以,对于一个大小为 n 的子树 u,有 2disu+11n

利用这一性质,我们可以进行合并操作。合并时,只需要维护堆性质和左偏树性质即可。

int fa[N], ls[N], rs[N], val[N], dis[N];
int find(int x){
if(fa[x] ^ x){
fa[x] = find(fa[x]);
}
return fa[x];
}
int merge(int x, int y){
if(!x || !y){
return x+y;
}
if(val[x] > val[y]){
swap(x, y);
}
rs[x] = merge(rs[x], y), fa[rs[x]] = x;
if(dis[ls[x]]<dis[rs[x]]) swap(ls[x], rs[x]);
dis[x] = dis[rs[x]]+1;
return x;
}

因为每次往下走一层,dis 会减少 1,而根据性质,dis 不会超过 log(size+1),因此,最坏情况(每次都交换)的复杂度为 O(log(m)+log(n)),其中 mn为合并的两棵子树的大小。

如何删除堆顶元素呢?只需要合并左右儿子。

void del(int x){
val[x] = -1 ,fa[ls[x]] = ls[x], fa[rs[x]] = rs[x];
fa[x] = merge(ls[x], rs[x]);
}
posted @   霜木_Atomic  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示