一个有趣的结构——Treap

来源:互联网 发布:个体户开淘宝企业店铺 编辑:程序博客网 时间:2024/05/21 09:18

如果将一个含有n个元素的集合插入到一课二叉查找树中,所得到的树可能会非常不平衡,从而导致查找时间过长。例如一个集合是一列逆序排列的有序序列,那么在按顺序将这个集合的元素插入到一棵空的二叉查找树时,就会导致这课二叉查找数是一棵只有左子树的树,查找起来的速度并没有像预想中的那样快。因此,一般来说,要为一组固定的元素建立一棵较为平衡的二叉排序树的话,可以采用一种策略就是先随机排列这些元素,然后再按照排列完的顺序将它们插入到树中,这就是随机建立二叉排序树。

但是,如果我们一次只可以得到集合中的一个元素,那么就没办法说先随机地排列他们了,这时候要怎么办,才可以保证创建出来的二叉排序树,可以不要太过于不平衡呢?

接下来就是这次的主角,Treap结构出场的时候了。这个结构也是一课二叉树。一棵treap是一棵修改了结点顺序的二叉排序树。树中的每个结点除了有一个关键字值key[x]外,还有一个数据域优先级priority[x],它是一个独立选取的随机数。

假设所有的优先级都是不同的,所有的关键字也都是不同的。

那么Treap这个结构,它的结点排列成让关键字遵循二叉排序树的性质,并且优先级遵循最小堆的性质:

  • If v is a left child ofu, thenkey[v]<key[u].

  • If v is a right child ofu, thenkey[v]>key[u].

  • If v is a child ofu, thenpriority[v]>priority[u].

这也是这个结构的名称为什么叫Treap的原因,既有tree又有heap。


上面是一个例子,每个结点中都有两个域,左边是关键字值,右边是优先级。

可以证明treap的期望高度是lgn,所以在treap内查找一个值所花的时间为[c1*lgn,c2*lgn];

接下来看看怎么在这个结构中进行几个基本操作。

搜索就不用说了,和一般的二叉排序树一样。

插入的话,先和一般的二叉排序树一样,找到可插入的位置,并插入,插入后,需要再调用一个辅助函数,来维护这个结构的优先级的性质,也就是要维护最小堆的性质。

从该结点一直向上调整,调整的过程是利用旋转来实现的,依旧是左旋转和右旋转。

判断该结点和它的父节点的优先级随机数的值,如果小于父节点的优先级的话,那么就要通过旋转来调整他们之间的位置,

当该节点是父节点的左孩子时,就用右旋转,是父节点的右孩子时,就用左旋转。

调整的终止条件就是,当它的父节点为空时(规定根节点的父节点为空),或者已经满足该结点的优先级随机数大于父节点的优先级随机数。

删除操作的话,就和二叉排序树的一样,在删除的时候,当该结点左右子树都存在的时候,需要用该结点的直接后继或者直接前驱的值来给这个结点,然后再删除它的直接后继或者直接前驱,在赋值的时候,只要将该结点的直接后继或者直接前驱的关键字值复制就行,优先级随机数不复制,这样在删除了之后,treap的各个结点的优先级性质没有改变,不需要再对它进行调整,可以节省一些时间。

由于这个结构的实现只是对BST进行一些简单的修改而已,各个基本操作的思想也不复杂,所以在这里就不给出实现代码了,因为我自己也没写。有兴趣的朋友可以写一下。

最后再贴出插入操作过程的几张图片:


最后在插入F:2这个结点的时候,图中并没有给出调整过程,大家可以在纸上自己画一下。个人还是觉得这个结构挺有趣的,所以介绍给大家认识。


原创粉丝点击