《数据结构、算法与应用 —— C++语言描述》学习笔记 — 优先级队列 — 左高树
一、左高树
前面所学习的堆结构是一种隐式数据结构。用完全二叉树表示的堆在数组中式隐式存储的(即没有明确的指针或其他数据能够用来重塑这种结构)。由于没有存储结构信息,这种表示方法的空间利用率很高,它实际上没有浪费空间。而且它的时间效率也很高。尽管如此,它并不适合于所有优先级队列的应用,尤其是当两个优先级队列或多个长度不同的队列需要合并时,这时我们就需要其他数据结构了,比如左高树。
1、外部节点
考察一棵二叉树,它有一类特殊的节点叫做外部节点,它代替树中的空子树。其余节点叫做内部节点。增加了外部节点的二叉树被称为扩充二叉树。如图所示,灰色方框节点表示外部节点:
2、高度优先左高树
令 s(x) 表示从节点 x 到其子树的外部节点的所有路径中最短的一条。根据 s(x) 的定义,若 x 是外部节点,则 s 的值为0;若 x 为内部节点,则 s 的值为: m i n { s ( L ) , s ( R ) } + 1 min\{s(L),s(R)\}+1 min{
s(L),s(R)}+1
其中,L 与 R 分别为 x 的左右孩子。扩充二叉树中各节点的 s 值如图所示:
(1)定义
一棵二叉树被称为高度优先左高树(HBLT),当且仅当任何一个内部节点的左孩子的 s 值都大于或等于右孩子的 s 值。
上图所示的二叉树不是HBLT。考察外部节点 a 的父节点,它的左孩子的 s 值为0,而右孩子的值为1,尽管其他内部节点均满足HBLT的定义。若将节点 a 的父节点的左右子树交换,则该树成为HBLT。
(2)特性
令 x 为HBLT的一个内部节点,则:
① 以x 为根的子树的节点数目至少为 2 s ( x ) − 1 2^{s(x)}-1 2s(x)−1
② 若以 x 为根的子树有 m 个节点,那么 s(x) 最多为 log 2 ( m + 1 ) \log_2{(m+1)} log2(m+1)
③ 从 x 到一外部节点的最右路径的长度为 s(x)。
(3)HBLT 与 大小根树
若一棵 HBLT 同时还是大根树,则称为 最大HBLT。若一棵 HBLT 同时还是小根树,则称为 最小HBLT。
最大优先级队列可以用最大HBLT表示,最小优先级队列可以用最小HBLT表示。
3、重量优先左高树
如果我们考虑的不是路径长度,而是节点数目,那么我们可以得到另一种左高树。定义重量 w(x) 是以节点 x 为根的子树的内部节点数目。若 x 是外部节点,则它的重量为0;若 x 是内部节点,则它的重量是其孩子节点的重量之和加1。扩充二叉树中的各节点重量如图:
一棵二叉树被称为重量优先左高树(WBLT),当且仅当其任何一个内部节点的左孩子的 w 值都大于或等于右孩子的 w 值。若一棵若一棵 WBLT 同时还是大根树,则称为 最大WBLT。若一棵 WBLT 同时还是小根树,则称为 最小WBLT。
同 HBLT 类似,具有 m 个节点的 WBLT 的最右路径长度最多为 log 2 ( m + 1 ) \log_2{(m+1)} log2(m+1)。使用 WBLT 或 HBLT,可移植性优先级队列的查找、插入、删除操作,其时间复杂性与堆相同。和堆一样,WBLT 与 HBLT 可以在线性时间内完成初始化。用 WBLT 或 HBLT 表示的两个优先级队列可以在对数时间内合并为一个,而用堆表示的优先级队列做不到这一点。
下面我们以 HBLT 为例介绍相关操作的实现,WBLT 与之类似。
4、最大HBLT的插入
最大HBLT的插入操作可以利用最大HBLT的合并操作来实现。假定将元素 x 插入名为 H 的最大HBLT中。如果构建一棵仅有一个元素 x 的最大 HBLT ,然后将它与 H 进行合并,那么合并后的最大HBLT将包含 H 的全部元素和元素 x。因此,要插入一个元素,可以先建立一棵新的只包含这个元素的HBLT,然后将这棵新的HBLT与原来的HBLT合并。
5、最大HBLT的删除
最大元素在根中。若根被删除,则分别以左右孩子为根的子树是两棵最大HBLT。将这两棵最大HBLT合并,便是删除后的结果。因此,删除操作可以通过删除根元素之后的两棵子树的合并来实现。
6、两棵最大HBLT的合并
具有 n 个元素的HBLT,其最右路径的长度为 O ( l o g n ) O(log n) O(logn),一个算法要合并两棵HBLT,只能在遍历其最右路径中进行。由于在每个节点上实施合并所时间为 O ( 1 ) O(1) O(1),所以合并算法的时间复杂性是合并后节点数的对数。因此算法从两棵HBLT的根开始仅沿右孩子移动。
合并策略最好用递归来实现。令 A、B 为需要合并的两棵最大HBLT。若一个为空,则另一个便是合并的结果。假设两者均不为空。为实现合并,先比较两个根元素,较大者作为合并后的根。假定 A 的根较大,且左子树为 L。令 C 是 A 的右子树与 B 合并而成的 HBLT。A 与 B 合并的结果是以 A 为根,以 L 和 C 为子树的最大HBLT。如果 L 的 s 值小于 C 的 s 值,则 C 为左子树,否则 L 为左子树。
7、HBLT的初始化
初始化过程是将 n 个元素逐个插入最初为空的最大HBLT,所需时间为 O ( n l o g n ) O(nlog n) O(nlogn)。为得到具有线性时间的初始化算法,我们首先创建 n 个仅含一个元素的最大HBLT,这 n 棵树组成一份 FIFO 队列,然后从队列中依次成对删除HBLT,然后将其合并后再插入队列末尾,直到队列只有一棵HBLT为止。
二、左高树实现
我们这里通过继承前面实现的链式二叉树实现左高树,因为左高树并不需要存储额外的数据。
1、链式二叉树修改
链式二叉树的修改包括:
(1)私有成员变为受保护成员:
template<typename T>
class linkedBinaryTree : public binaryTree<T>
{
protected:
binaryTreeNode<T>* root = nullptr;