splay树
来源:互联网 发布:贪心算法 最优解 编辑:程序博客网 时间:2024/05/21 22:32
splay树,中文名一般叫做伸展树。和treap树相同,作为平衡树,它也是通过左旋和右旋来调整树的结构。
和treap树不同的是,splay树不再用一个随机的权值来进行平衡,而是用固定的调整方式来使得调整之后的树会比较平衡。在左旋右旋的基础上,splay树定义了3个操作。
1. Zig
直接根据x节点的位置,进行左旋或右旋。该操作将x节点提升了一层。
2. Zig-Zig
若p不是根节点,还有父亲节点g,且p和x同为左儿子或右儿子,则进行Zig-Zig操作:当x,p同为左儿子时,依次将p和x右旋;当x,p同为右儿子时,依次将p和x左旋。注意此处不是将x连续Zig两次。该操作将x节点提升了两层。
3. Zig-Zag
若p不是根节点,还有父亲节点g,且p和x不同为左儿子或右儿子,则进行Zig-Zag操作:当p为左儿子,x为右儿子时,将x节点先左旋再右旋;当p为右儿子,x为左儿子时,将x节点先右旋再左旋。该操作将x节点提升了两层。
进一步在Zig,Zig-Zig和Zig-Zag操作上,splay树定义了splay操作。对于x以及x的祖先y,splay(x, y)表示对x节点进行调整,使得x是y的儿子节点。
splay(x,y):While(x.father!=y)p=x.fatherIf(p.father==y) Then//因为p的父亲是y,所以只需要将x进行Zig操作//就可以使得x的父亲变为yIf(p.left==x) Thenright-rotate(x)Elseleft-rotate(x)End IfElseg=p.fatherIf(g.left==p) ThenIf(p.left==x) Then//x,p同为左儿子,Zig-Zig操作right-rotate(p)right-rotate(x)Else//p为左,x为右,Zig-Zag操作left-rotate(x)right-rotate(x)End IfElseIf (p.right==x) Then//x,p同为右儿子,Zig-Zig操作left-rotate(p)left-rotate(x)Else //p为右,x为左,Zig-Zag操作right-rotate(x)left-rotate(x)End IfEnd IfEnd IfEnd While在执行这个操作的时候,需要保证y节点一定是x节点祖先。值得一提的是,大多数情况下我们希望通过splay操作将x旋转至整棵树的根节点。此时只需令y=NULL即可实现。
splay树的插入和查询操作和普通的二叉搜索树没有什么大的区别,需要注意的是每次插入和查询结束后,需要对访问节点做一次splay操作,将其旋转至根。
<pre name="code" class="cpp">insert(key):node=bst_insert(key) //同普通的BST插入,node为当前插入的新节点splay(node, NULL)find(key):node=bst_find(key) //同普通的BST查找,node为查找到的节点splay(node,NULL)同时由于splay的特性,我们还有两个特殊的查询操作。在树中查找指定数key的前一个数和后一个数。我们先将key旋转至根,那么key的前一个数一定是根节点左儿子的最右子孙,同时key的后一个数一定是根节点右儿子的最左子孙。
findPrev(key):splay(find(key),NULL)node=root.leftWhile(node.right)node=node.rightReturn nodefindNext(key):splay(find(key),NULL)node=root.rightWhile(node.left)node=node.leftReturn nodesplay的删除可以采用和一般二叉搜索树相同的方法:即先找到节点key,若key没有儿子则直接删去;若key有1个儿子,则用儿子替换掉x;若key有2个儿子,则通过找到其前(或后)一个节点来替换掉它,最后将该节点splay到根。同时,这里还有另一种方法来完成删除操作:首先我们查找到key的前一个数prev和后一个数next。将prev旋转至根,再将next旋转为prev的儿子。此时key节点一定是next的左儿子。那么直接将next的左儿子节点删去即可。
delete(key):prev=findPrev(key)next=findNext(key)splay(prev,NULL)splay(next,prev)next.left=NULL这里你可能会担心如果key是数中最小或者是最大的数怎么办?一个简单的处理方式是手动加入一个超级大和超级小的值作为头尾。这里有一个问题,假如要删除一个区间[a,b]的数该怎么做?因为要删除[a,b],那么我就要想办法把[a,b]的数旋转到一个子树上,再将这个子树删掉就行了。方法和删除一个数相同,我首先将a的前一个数prev和b的后一个数next找出来。同样将prev旋转至根,再将next旋转为prev的儿子。那么此时next的左子树一定就是所有[a,b]之间的数了!
deleteInterval(a,b):prev=findPrev(a)next=findNext(b)splay(prev,NULL)splay(next,prev)如果a,b不在树中,把a,b插入树中,做完之后再删除。
splay树由于splay操作的使得其相较于treap树具有更大的灵活性,并且不再有随机性。其插入、查找和删除操作的均摊时间复杂度也都是O(logn)的。
0 0
- SPLAY树
- Splay树
- splay树
- splay树
- Splay 树
- splay树
- Splay树
- SPLAY树
- 【splay】splay小结 文艺平衡树
- 伸展树(splay树)
- Splay树(伸展树)
- Splay Tree(伸展树)
- Splay树简介
- Splay Tree 伸展树
- Splay伸展树&模板
- 【splay树】hdu 3487
- Splay 伸展树
- 伸展树splay tree
- 14.express 的工程的结构
- Length of Last Word
- Express 4.x API
- 考研准备
- 原来Github上的README.md文件这么有意思——Markdown语言详解
- splay树
- Doxygen使用教程(个人总结)
- Codevs 1036 商务旅行
- 15.REST 风格的路由规则
- 被公司辞了
- myeclipse使用经验---生成WAR包并在Tomcat下部署发布
- 2016年6月27日--2016年7月8日(1小时,剩2939小时)
- java设置一段代码执行超时时间
- apache所有开源项目文件