之前我们讲到二叉搜索树,从二叉搜索树到2-3树到红黑树到B-树。
二叉搜索树的主要问题就是其结构与数据相关,树的深度可能会很大,Treap树就是一种解决二叉搜索树可能深度过大的另一种数据结构。
Treap
Treap=Tree+Heap。Treap本身是一棵二叉搜索树,它的左子树和右子树也分别是一个Treap,和一般的二叉搜索树不同的是, Treap纪录一个额外的数据,就是优先级。Treap在以关键码构成二叉搜索树的同时,还满足堆的性质。 这些优先级是是在结点插入时,随机赋予的,Treap根据这些优先级满足堆的性质。这样的话,Treap是有一个随机附加域满足堆的性质的二叉搜索树,其结构 相当于以随机数据插入的二叉搜索树。其基本操作的期望时间复杂度为O(logn)。相对于其他的平衡二叉搜索树,Treap的特点是实现简单,且能基本实现随机平衡的结构。
插入
给节点随机分配一个优先级,先和二叉搜索树的插入一样,先把要插入的点插入到一个叶子上,然后 跟维护堆一样,如果当前节点的优先级比根大就旋转,如果 当前节点是根的左儿子就右旋,如果当前节点是根的右儿子就左旋。删除
删除一个节点有两种方式,可以像删除二叉树节点那样删除一个节点,也可以像删除堆中节点那样删除。
1、用二叉搜索树的方式删除关于查找最小值:
基本方法就是从子树的根节点开始, 如果左子节点不为空,那么就访问左子节点,直到左子节点为空,当前节点就是该子树的最 小值节点。删除它只需用它的右子节点代替它本身。
2、用堆的方式删除
因为Treap满足堆性质,所以只需要把要删除的节点旋转到叶节点上,然后直接删除就可以了。
具体的方法:
如果该节点的左子节点的优先级小于右子节点的优先级,右旋该节点,使该节点降为右子树的根节点,然后访问右子树的根节点,继续操作;
反之,左旋该节点,使该节点降为左子树的根节点,然后访问左子树的根节点,继续操作,直到变成可以直接删除的节点。
(即:让小优先级的结点旋到上面,满足堆的性质)
删除最多进行O(h)次旋转,期望复杂度是O(logn)。
查找
和一般的二叉搜索树一样,但是由于Treap的随机化结构,Treap中查找的期望复杂度是O(logn)。
对比
与 Splay树 相比:
维护子树的大小
举个栗子
Treap 是一种高效的动态的数据容器,据此我们可以用它处理一些数据的动态统计问题。
一、一个应用实例
[问题描述] 有一个游戏排名系统,通常要应付三种请求:上传一条新的得分记录、查询某个玩家的当前 排名以及返回某个区段内的排名记录。当某个玩家上传自己最新的得分记录时,他原有的得 分记录会被删除。为了减轻服务器负担,在返回某个区段内的排名记录时,最多返回 10 条 记录。
[求]
(1)更新玩家的得分记录
(2)查询玩家排名(如果两个玩家的得分相同, 则先得到该得分的玩家排在前面。)
(3)查询第 Index 名开始的最多 10 名玩家名字
[解]
因为作为字符串的姓名是不便于处理的,我们给每个玩家都制定一个ID,首先要建立一个由姓名到玩家ID的映射数据结构。为了查找快速,可以用Trie树。之后我们建立一个双关键字的Treap,关键字1为得分从小到大,关键字2为时间戳从大到小,这种排列方式的逆序,恰好是我们要的顺序(也可以直接就是逆序)。
对于问题(1),先查询玩家是否已经存在,如果已经存在,在Treap中更新对应已经存在的记录。
对于问题(2),就是基本的求排名操作。
对于问题(3),就是分别查找第(总记录数 + 1 – k)小的记录。
二、双端优先队列的实现
优先队列(Priority Queue)是一种按优先级维护进出顺序的数据容器结构,可以选择维护实现取出最小值或最大值。我们通常用堆实现优先队列,通常取出最值的时间复杂度为 O(logN)。
用最小堆可以实现最小优先队列,用最大堆可以实现最大优先队列。但是如果我们要求一种 “双端优先队列”,即要求同时支持插入、取出最大值、取出最小值的操作,用一个单纯的堆就不能高效地实现了。
(可以用两个堆来实现,两堆中的元素都互指,但维护两个堆比较复杂。)
我们可以方便地使用Treap实现双端优先队列,只需建立一个 Treap,分别写出取最大值和最小值的功能代码就可以了, 无需做任何修改。由于Treap平衡性不如堆完美,但期望时间仍是 O(logN)。更重要的是在 实现的复杂程度上大大下降,而且便于其他操作的推广。所以,用 Treap 实现优先队列不失为一种便捷而又灵活的方法。
其它:
平衡树并不适合作为所有数据类型的数据的有序存储容器,因为可能有些类型的两个元素直接相互比较大小是十分 耗时的,这个常数时间的消耗是无法忍受的。例如字符串,作为检索字符串的容器,我们更推荐Trie树,而不是平衡树。平衡树仅适合做元素间相互比较时间很少的类型的有序存储容器。
【参考】
中文维基百科http://zh.wikipedia.org/wiki/%E6%A0%91%E5%A0%86