我们知道,二叉查找树相对来说比较容易形成最坏的链表情况,所以前辈们想尽了各种优化策略,包括AVL,红黑,以及今天要讲的Treap树。
Treap树是什么?
Treap=Tree+Heap
treap是heap和tree结合,中文名叫树堆。
首先它每个节点有2个值value和weight,其中只看weight的话,满足heap二叉堆的特性(父亲比儿子都小/大),只看value的话,满足排序二叉树特性(以左儿子为根的子树元素都比父亲小,右儿子为根的子树都比父亲大),value是要维护的值,weight是随机生成的值。由于随机生成的堆使整棵treap变得平衡,所以treap是一种比较短小精悍的平衡树的实现。
添加
首先我们知道各个节点的“优先级”是采用随机数的方法,那么就存在一个问题,当我们插入一个节点后,优先级不满足“堆定义"的时候我们该怎么办,前辈说此时需要旋转,直到满足堆定义为止。
删除
跟普通的二叉查找树一样,删除结点存在三种情况。
①:叶子结点
跟普通查找树一样,直接释放本节点即可。
②:单孩子结点
跟普通查找树一样操作。
③:满孩子结点
其实在treap中删除满孩子结点有两种方式。
第一种:跟普通的二叉查找树一样,找到“右子树”的最左结点,拷贝元素的值,但不拷贝元素的优先级,然后在右子树中 删除结点即可。
第二种:将”结点下旋“,直到该节点不是”满孩子的情况“,该赋null的赋null,该将孩子结点顶上的就顶上。
总结
treap树在CURD中是期望的logN,由于我们加了”优先级“,所以会出现”链表“的情况几乎不存在,但是他的Add和Remove相比严格的。
平衡二叉树有更少的旋转操作,可以说性能是在”普通二叉树“和”平衡二叉树“之间。
ps:
只要无环无向联通图都叫树,具体就是n个点n-1条无向边连接且任意两点联通的一种拓扑结构,如果我们选定一个节点作为根,那么这棵树就是有根树,遍历一遍就可以确定所有的父亲-儿子的关系了,如果一棵有根树的每一个结点至多有两个儿子,那么这棵树称为二叉树。
如果一棵二叉树的每一个节点都带着一个值,且父亲的值总是比儿子的值要大,我们称这棵树为大顶二叉堆,如果是父亲比儿子都要小,那就是小顶二叉堆,统称为二叉堆。
(其实一般都把二叉两个字省略掉,毕竟通常说的堆都是二叉堆,然而堆不止二叉堆)。这一个良好的性质注定了堆可以用来当作优先队列使用。
优先队列支持以下操作
1.放一个元素进去
2.总是能取出一个最大的元素出来(大,小的规矩可以通过一个比较函数来定义)
显然堆就是可以这么做。当然啦,之前说过堆不止二叉堆,还有更复杂的二项堆,斐波那契堆,配对堆等等。
所以,堆是一种特殊的树。
参考:
https://www.zhihu.com/question/36134980/answer/66080662
http://www.cnblogs.com/huangxincheng/archive/2012/07/30/2614484.html