阅读信息
约 1268 个字 7 分钟 本页总访问量:加载中... 次
ADS Lec 04 左偏堆和斜堆
左偏堆
概念
dist(或 Npl):对于任意一个节点,它的 dist 为这个节点到没有两个孩子的节点的最短距离。
对叶节点、只有一个孩子的节点,dist 都为 0。
左偏堆:
- 是二叉堆
- 对任意节点,其左孩子的 dist 一定大于等于右孩子的 dist
左偏堆的性质:
- 任意节点的 dist = 其右孩子的 dist + 1(如果没有右孩子,则为 0)。
- 若根节点的 dist 为 N,则前 N+1 层为满二叉树,整个左偏堆至少有\(2^{N+1}-1\)个节点。
- 若整个左偏堆共 N 个节点,则右侧路径最多有\(\lfloor \log(N+1) \rfloor\)个节点。
左偏堆最右侧的路径尽可能短,合并时只沿右侧路径合并,时间复杂度低。
操作
节点的定义为:
递归合并
- 将已经合并的顶点(记为 o)从根较小的堆开始,沿最右侧不断下移。
- 每次有两个待合并的堆,分别为 o 的右儿子和另一个左偏堆。将这两者中根较小的作为 o 的右儿子。
- 检查“放在 o 的右儿子”这一步是否违反左偏性质,调整并更新 o 的 dist。
- 将 o 下移,递归进行。
代码:
C++ | |
---|---|
迭代合并
迭代合并中,用栈存储合并路径上的所有父节点。合并完后,回溯存储的父节点,依次调整左右儿子,使其保持左偏。
- 递归合并是自底向上进行,先产生最底部的合并结果,每次产生后维护左偏
-
迭代合并是自顶向下进行,先合并完所有节点,再从下开始依次调整、维护左偏
-
将已经合并的顶点(记为 o)从根较小的堆开始,沿最右侧不断下移。
- 若另一个堆小于 o 的右儿子,则交换到 o 右儿子的位置。
- 在栈中存储合并的父节点 o。
- 将 o 下移,递归进行。
- 依次弹栈,维护左偏性质。
代码:
插入
单点插入可看作原先的左偏堆和只有一个节点的新堆的合并。
代码:
C++ | |
---|---|
删除
删除一个节点时,需要考虑它的父节点和子节点。
先将子节点对应的子树合并,再用合并后的树代替要删除的节点。
代码:
C++ | |
---|---|
斜堆
概念
左偏堆和斜堆的关系类似于 AVL 树和 Splay 树。
斜堆满足任何节点小于(或大于)其左右儿子,但不需要像左偏堆一样维护 dist。
斜堆更看重合并的效率,而不是堆的平衡,它的目的是使 M 次操作的最大时间复杂度为\(O(M\log N)\)。
操作
节点定义:
递归合并:
顶小的堆原先的右子树、另一个堆作为待合并的两个堆,合并后作为顶小的堆的右子树。再将顶小的堆的左右子树交换。
如果一个堆和空节点合并,则这个堆的左右子树不交换。
递归合并代码
迭代合并:
用栈记录每次操作的父节点。所有合并完成后,从下到上依次交换栈中节点的左右节点。
迭代合并代码
插入:
看作和只有一个节点的堆的合并。
删除:
合并左右儿子,用合并后的堆代替要删除的节点。
摊还分析
回顾势能分析:
对于状态\(D_i\),需要构造势能函数\(\Phi(D_i)\),满足\(\Phi(D_k)\ge \Phi(D_0)\)。
每一步的摊还成本\(\hat{c_i}\) \(=\) 实际成本\(c_i + \Phi(D_i)-\Phi(D_{i-1})\)。
总的摊还成本\(\displaystyle\sum_{i=1}^n\hat{c_i}\) \(=\) 总的实际成本\(\displaystyle\sum_{i=1}^n c_i+ \Phi(D_n)-\Phi(D_0)\ge \displaystyle\sum_{i=1}^n c_i\)
整个操作中单调递增的函数不适合作为势能函数。因为势能函数反映实际成本和均摊成本之间的差距,一定有正有负。
斜堆合并的分析:
每次斜堆合并的操作步数,即实际成本,取决于最右侧路径节点数之和。但斜堆不保证左偏,右路径长度可能为\(O(N)\)。
定义重节点:对于一个节点,在以这个节点为根(包含该节点)的子树中,如果右子树的节点数大于等于总节点数的一半,则该节点为重节点;反之为轻节点。
定义势能函数:势能为整棵树中重节点的个数。
斜堆合并时,只有最右路径上节点的轻重情况会改变。其他节点的轻重情况一定不变(因为直接从左子树移动到右子树,局部结构不变)。所以考虑势能变化,只要考虑最右路径的轻重节点数。
最右路径上,重节点一定变为轻节点,而轻节点不一定变为重节点(本身交换后就不一定,左儿子还可能加其他节点)。
对于右路径上的轻节点,左子树的节点较多,类似左偏堆,轻节点最多有\(\log(N)\)个。
记\(l_1\)、\(h_1\),\(l_2\)、\(h_2\)为第一、二个堆最右路径上轻、重节点数,\(h\)表示其他位置的重节点数。
一次操作前:\(\Phi_{i-1}=h_1+h_2+h\)
一次操作后:\(\Phi_{i}\le l_1+l_2+h\)
一次操作的均摊成本:
由此得到,斜堆合并的均摊成本为\(O(\log N)\)。