(来自算法导论十七章摊还分析思考题 17−3,「摊还加权平衡树」)
想当年替罪羊树可能是我第一个学习的平衡树。。。但是很少有人说明它均摊 O(lgn) 的效率是从何而来,正巧在看算导的时候有这样一个题,遂开写。
这玩意是一种 Weighted Balance Tree,靠重构来维护一个均摊较好的性能。
具体地说,选一个在 12≤α<1 之间的一个常数 α 作为平衡因子。如果一个节点 u 的左右子树的大小都不大于 αsize(u),那么我们认为 u 是 α 平衡的,若所有节点都是 α 平衡的那么这棵树就是 α 平衡的。
首先说明如果 T 对于 α (12≤α<1) 是平衡的,那么这棵树的树高是 O(lgn)。对于一个点 u 从根开始向下走,每向下走一步那么以它为根的子树大小至多乘上 α,叶子的大小为 1,即 nαh=1,得到 h=logα1n=log1αn=O(lgn)。
如此我们就知道了一棵平衡的替罪羊树它的查询效率是 O(lgn) 的,接下来我们着重分析它是如何在插入操作中维护平衡。
对于节点 u∈T 及它的左右儿子 ul,ur 定义
Δ(u)=|size(ul)−size(ur)|
定义势能函数
Φ(T)=∑u∈T,Δ(u)≥2Δ(u)2α−1
首先证一个引理是对于 α=12 平衡的 T 的势能为 0。若势能大于 0 则存在 u∈T 有 |size(ul)−size(ur)|≥2,不妨设是 size(ul)−size(ur)≥2,由于它是 12 平衡的,故有 size(ul)≤size(u)2=size(ul)+size(ur)+12 即 size(ul)≤size(ur)+1,带到最初的式子中有 size(ur)+1−size(ur)≥2 即 1≥2 导出矛盾。
重构操作在 O(size(u)) 的时间下将 subtree(u) 重构为 12 平衡的,这可以直接像类似线段树建树的分治方法做到。下面我们将证明重构操作是均摊 O(1) 的。
重构操作的均摊代价为
^c=O(size(u))+ΔΦ(T)=O(size(u))+ΔΦ(subtree(u))=O(size(u))+0−∑v∈subtree(u),Δ(v)≥2Δ(v)2α−1
我们考察 u 的情况,它是 α 不平衡的,不妨设 size(ur)>size(ul),则有
Δ(u)=size(ur)−size(ul)≥αsize(u)+1−(1−α)size(u)+1=(2α−1)size(u)+2
它是大于 2 的,故我们想用它来支付掉均摊代价
^c≤O(size(u))−Δ(u)2α−1≤O(size(u))−(2α−1)size(u)2α−1=O(1)
最终得到重构的均摊代价是 O(1) 的。
接下来考虑分析插入,维护 α 平衡的情况下,其实际代价是树高 h=O(lgn),插入一个点会使一条链上的 Δ 均增或减 1,而这个增量也不会超过树高 h。
^c=h+ΔΦ(T)≤h+h2α−1=(1+12α−1)h=O(lgn)
综上我们这样维护替罪羊树:如普通二叉搜索树的方式一样插入查询,插入完成时检查是否存在点不满足 α 平衡,若存在则找到深度最低的不平衡点再将以其为根的子树进行重构,这样可以保证在插入和查询的时候整棵树是 α 平衡的。根据上面的分析,所有的操作都将在 O(lgn) 的均摊效率下运行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话