一 点睛
Treap 指 Tree + heap,又叫作树堆,同时满足二叉搜索树和堆两种性质。二叉搜索树满足中序有序性,输入序列不同,创建的二叉搜索树也不同,在最坏的情况下(只有左子树或只有右子树)会退化为线性。例如输入1 2 3 4 5,创建的二叉搜索树如下图所示。
二叉搜索树的插入、查找、删除等效率与树高成正比,因此在创建二叉搜索树时要尽可能通过调平衡压缩树高。平衡树有很多种,例如 AVL 树、伸展树、SBT、红黑树等,这些调平衡的方法相对复杂。
若一个二叉搜索树插入节点的顺序是随机的,则得到的二叉搜索树在大多数情况下是平衡的,即使存在一些极端情况,这种情况发生的概率也很小,因此以随机顺序创建的二叉搜索树,其期望高度为O(logn )。可以将输入数据随机打乱,再创建二叉搜索树,但我们有时
并不能事前得知所有待插入的节点,而 Treap 可以有效解决该问题。
Treap 是一种平衡二叉搜索树,它给每个节点都附加了一个随机数,使其满足堆的性质,而节点的值又满足二叉搜索树的有序性,其基本操作的期望时间复杂度为 O(logn)。相对于其他平衡二叉搜索树,Treap 的特点是实现简单,而且可以基本实现随机平衡。
在 Treap 的构建过程中,插入节点时会给每个节点都附加一个随机数作为优先级,该优先级满足堆的性质(最大堆或最小堆均可,这里以最大堆为例,根的优先级大于左右子节点),数值满足二叉搜索树性质(中序有序性,左子树大于根,右子树小于根)。
输入 6 4 9 7 2,构建 Treap。首先给每个节点都附加一个随机数作为优先级,根据输入数据和附加随机数,构建的 Treap 如下图所示。
二 右旋和左旋
Treap 需要两种旋转操作:右旋和左旋。
1 右旋(zig)
节点 p 右旋时,会携带自己的右子树,向右旋转到 q 的右子树位置,q 的右子树被抛弃,此时 p 右旋后左子树正好空闲,将 q 的右子树放在 p 的左子树位置,旋转后的树根为 q 。
2 左旋(zag)
节点 p 左旋时,携带自己的左子树,向左旋转到 q 的左子树位置,q 的左子树被抛弃,此时 p 左旋后右子树正好空闲,将 q 的左子树放在 p 的右子树位置,旋转后的树根为 q 。
总结:无论是右旋还是左旋,旋转后总有一棵子树被抛弃,一个指针空闲,正好配对。
三 插入
Treap 的插入操作和二叉搜索树一样,首先根据有序性找到插入的位置,然后创建新节点插入该位置。创建新节点时,会给该节点附加一个随机数作为优先级,自底向上检查该优先级是否满足堆性质,若不满足,则需要右旋或左旋,使其满足堆性质。<