Java进阶:17.3 伸展树

1、伸展树简介

伸展树(Splay Tree)是特殊的二叉查找树(BST)。
它的特殊是指,它除了本身是棵二叉查找树之外,它还具备一个特点: 当某个节点被访问时,伸展树会通过旋转使该节点成为树根。这样做的好处是,下次要访问该节点时,能够迅速的访问到该节点。

通过之前的学习,知道连续m次查找,对于AVL树来说,共需要O(mlogn)时间。根据局部性原理,我们可以改进AVL树!!-----> 引入伸展树

局部性:刚被访问过的数据,极有可能很快地再次被访问。下一将要访问的节点,极有可能就在刚被访问过的节点附近。

1.2、特性

1.和普通的二叉查找树相比,具有任何情况下、任何操作的平摊O(log2n)的复杂度,时间性能上更好
2.和一般的平衡二叉树比如 红黑树、AVL树相比,维护更少的节点额外信息,空间性能更优,同时编程复杂度更低
3.在很多情况下,对于查找操作,后面的查询和之前的查询有很大的相关性。这样每次查询操作将被查到的节点旋转到树的根节点位置,这样下次查询操作可以很快的完成
4.可以完成对区间的查询、修改、删除等操作,可以实现线段树和树状数组的所有功能

2、 性能评价

无需记录节点高度或者平衡因子,编程简单——优于AVL树
分摊复杂度O(logN) —— 与AVL树相当
局部性强、缓存命中率极高时(即k<<n<<n)
效率甚至可以更高——自适应的O(logK)
任何连续的m次查找,都可在O(mlogk + nlogn)时间内完成

但是
仍不能保证单词最坏情况的出现
不适用于对效率敏感的场所

3、逐层伸展( X )

伸展方式有两种:1 逐层伸展 2双层伸展
逐层伸展:一步一步往上,自下而上,逐层单旋。直到V最终被推送至根。
但效率很低,最坏情况:对于下图依次访问1~7的话,每一周期总时间O(n^2),分摊时间O(n)。远小于AVL树的O(logN)
在这里插入图片描述
Zig-zig / zag-zag 的逐层旋转:
在这里插入图片描述

4、双层伸展 ( √ )

双层伸展:向上追溯两层,而非一层! (Tarjan提出)

实现O(log2n)量级的平摊复杂度依靠每次对伸展树进行查询、修改、删除操作之后,都进行旋转操作 Splay(x, root),该操作将节点x旋转到树的根部。

优点:① 折叠效果:一旦访问坏节点,对应路径的长度将随机减半
② 最好情况不会持续发生,单趟伸展操作,分摊O(logN)时间
在这里插入图片描述
伸展树的旋转有六种类型,如果去掉镜像的重复,则为三种:
zig(zag)、zig-zig(zag-zag)、zig-zag(zag-zig)。

4.1 zig- zig 旋转

如下图所示。在逐层伸展方式中,先VP旋转,再VG旋转。在双层旋转中,先对祖父节点G进行PG的越级旋转,再进行VP的旋转!!!(和第3部分的图对比一下区别)
在这里插入图片描述
优点:在一棵退化成单链的伸展树中访问其最深的节点,经过伸展后树高大约为原先的1/2

4.2 zig- zag 旋转

如下图所示,VPG三者在之字型链上。此时,先对P节点进行PV的zig旋转,再对G节点进行GV的zag旋转,最后变为右图所示,V成为P和G的祖先节点。这个方法和AVL树双旋完全等效,与逐层伸展别无二致。
在这里插入图片描述

4.3 zig / zag 旋转

如果V只有父亲,没有祖父。就会出现下图所示。Parent(v) = root(T) 在每轮调整中,这种情况至多(在最后)出现一次。
在这里插入图片描述

5、节点

public class mySplayTree<AnyType extends Comparable<? super AnyType>>
{
   
    /**
     * Construct the tree.
     */
    public mySplayTree( )
    {
   
        nullNode = new BinaryNode<AnyType>( null );
        nullNode.left = nullNode.right = nullNode;
        root = nullNode;
    }

    private BinaryNode<AnyType> newNode = null;  // Used between different inserts
    private BinaryNode<AnyType> header = new BinaryNode<AnyType>( null );
    private BinaryNode<AnyType> root;
    private BinaryNode<AnyType> nullNode;
}

6、查找

不论成功与否,总会把最后被访问的节点伸展到根。 —> 动态操作(回想一下之前学的二叉树、BVL树的查找都是静态操作噢)

    private BinaryNode<AnyType> splay( AnyType x, BinaryNode<AnyType> t )
    {
   
        BinaryNode<AnyType> leftTreeMax, rightTreeMin;

        header.left = header.right = nullNode;
        leftTreeMax = rightTreeMin = header;

        nullNode.element = x;   // Guarantee a match

        for( ; ; )
        {
   
            int compareResult = x.compareTo( t.element );

            if( compareResult < 0 )
            {
   
                if( x.compareTo( t.left.element ) < 0 )
                    t = rotateWithLeftChild( t );
                if( t.left == nullNode )
                    break;
                // Link Right
                rightTreeMin.left = t;
                rightTreeMin = t;
                t = t.left;
            }
            else if( compareResult > 0 )
            {
   
                if( x.compareTo( t.right.element ) > 0 )
                    t = rotateWithRightChild( t );
                if( t.right == nullNode )
                    break;
                // Link Left
                leftTreeMax.right = t;
                leftTreeMax = t;
                t = t.right;
            }
            else
                break;
        }

        leftTreeMax.right = t.left;
        rightTreeMin.<
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值