伸展树-搜索树

伸展树-搜索树

伸展树 也叫自适应查找树

伸展树实质上是一个二叉搜索树,包含插入、查询、删除等二叉搜索树所有操作,这些操作的时间复杂度为O(logN)

与平衡二叉树相比
1.平衡二叉树每个节点都要存储平衡信息(节点高度)
2.执行插入、删除操作后需要回复平衡,重新计算节点高度,操作复杂度高
3.对于简单的输入,性能提升并不明显

平衡二叉树提升性能的地方
1.平衡二叉树在最差的平均时间复杂度基本都是保持在O(logN)

伸展树原理:假设一个节点在一次访问后,这个节点很可能不久会被再次访问。
伸展树的做法是在每一次访问一个节点后,就通过一些列操作把这个节点挪移到树根位置
尽管最坏情况下单次查询时间复杂度会达到 O(N),线性复杂度
而实际证明伸展树保证在 M 次连续搜索的过程中时间复杂度不大于O(M*logN)

伸展树:查询、插入、删除操作的逻辑如下

查询:
伸展树的查询继承二叉搜索树的查询
如果存在,则 找到节点
如果不存在,则 获取到一个跟 要查找数值最接近的节点
每次查询后都会通过伸展操作将 找到节点 或者 最接近的节点 伸展至树根

插入:
首先执行 查找 操作,如果找到 则返回
没找到,则创建新节点
情况一:
如果 新插入节点值 > Root的值,调整结构
newNode.LeftChild = Root 节点
newNode.RightChild = Root.RightChild
最后令 Root = newNode

情况二:
如果 新插入节点值 < Root 的值,调整结构
newNode.LeftChild = Root.LeftChild
newNode.RightChild = Root
最后令 Root = newNode

删除:
先执行查询操作,如果不存在,则返回
存在,此时有一个隐含信息,因为搜索到了该节点,那么这个节点此时已经被伸展到树根了
此时这个节点就是 Root 节点
情况一:
若无左子树,则直接删除

Root = Root.RightChild;
Root.ParentNode = null;
删除完成,返回

情况二:
若无右子树,也直接删除

Root = Root.LeftChild;
Root.ParentNode = null;
删除完成,返回

情况三:
左子树和右子树都有
BinNode tempRoot = Root; 存储树根节点

令一个节点 存储 根节点的 左子树
BinNode LTree = Root.LeftChild;
LTree.ParentNode = null; // 删除父节点
Root.LeftChild = null; / /暂时将左子树切除

Root = Root.RightChild; // 根节点 = 其右子树,也就是原本的根节点已经被删除了
Root.ParentNode = null; // 只保留右子树

Search(tempRoot.Element); //以原树根为目标,做一次(必定失败的)查找,因为跟节点已经被删除了
// 因为根节点被删除了,搜索会将右子树中节点值最接近查找数值的节点返回,这个节点就是右子树中最小的节点,然后右子树中最小节点必伸展至根,且(因无类同节点)其左子树必空

Root.LeftChild = LTree; // 令 新的根节点左子树 = 上面存储的左子树
LTree.ParentNode = Root; //只需将原左子树接回原位即可

删除完成,返回

关于查询操作中,将节点伸展至根节点的操作,应用伸展(splaying)技术
经过旋转操作,分为三种情况
1.zig 情况
X是查找路径上我们需要旋转的一个非根节点。
如果X的父节点是根,那么我们用下图所示的方法旋转X到根:
如下图
在这里插入图片描述

2.zig-zag 情况
在这种情况中,X有一个父节点P和祖父节点G(P的父节点)。X是右子节点,P是左子节点,或者反过来。这个就是双旋转。
先是X绕P左旋转,再接着X绕G右旋转。
在这里插入图片描述

3.zig-zig 情况
这和前一个旋转不同。在这种情况中,X和P都是左子节点或右子节点。
先是P绕G右旋转,接着X绕P右旋转。
在这里插入图片描述
C#实现代码如下

    /// <summary>
    /// 伸展树
    /// </summary>
    class SplayTree<T> : BSTree<T> where T : IComparable<T>
    {
        /// <summary>
        /// 查找:伸展树的查找也会引起整树的结构调整,固需重写
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public override BinNode<T> Search(T t)
        {
            BinNode<T> node = base.Search(t);

            BinNode<T> temp = (null != node) ? node : _hot;
            Root = Splay(temp);// 将最后一个被访问的节点伸展至跟

            return Root;
        }

        /// <summary>
        /// 插入
        /// </summary>
        public override BinNode<T> Insert(T t)
        {
            if (null == Root)  // 树为空
            {
                Root = new BinNode<T>(t);
                return Root;
            }

            BinNode<T> node = Search(t);
            // 首先 Search,如果找到 t 则返回,否则 Root 即为与 t 相近的节点
            if (null != node && node.Element.CompareTo(t) == 0) 
            {
                return Root;
            }
  
            BinNode<T> newNode = new BinNode<T>(t);

            // 创建新节点,以下调整 <=7 个指针以完成局部重构
            // 情况一:
            // 如果 新插入节点值 > Root的值,调整结构                  newNode
            // newNode.LeftChild = Root 节点                     ----------------
            // newNode.RightChild = Root.RightChild              |              |
            //                                                  Root       Root.RightChild
            // 最后令 Root = newNode

            // 情况二:
            // 如果 新插入节点值 < Root 的值,调整结构                  newNode
            // newNode.LeftChild = Root.LeftChild                  ----------------- 
            // newNode.RightChild = Root                           |               |
            //                                               Root.LeftChild       Root
            // 最后令 Root = newNode

            BinNode<T> tempRoot = Root;
            if (Root.Element.CompareTo(t) < 0)  //插入新根,以Root和Root.RC为左、右孩子
            {
                BinNode<T> rootRightChild = Root.RightChild;
                //2 + 3个
                newNode.LeftChild = tempRoot;
                tempRoot.ParentNode = newNode;

                newNode.RightChild = rootRightChild;
                if (null != rootRightChild)
                {
                    rootRightChild.ParentNode = newNode;
                    Root.RightChild = null;
                }
                Root = newNode; 
            }
            else  //插入新根,以Root.LeftChild和Root为左、右孩子
            {
                BinNode<T> rootLeftChild = Root.LeftChild;
                newNode.LeftChild = rootLeftChild;
                //2 + 3个
                if (null != rootLeftChild)
                {
                    rootLeftChild.ParentNode = newNode;
                    tempRoot.LeftChild = null;
                }

                newNode.RightChild = tempRoot;
                tempRoot.ParentNode = newNode;
                Root = newNode;
            }

            CheckNode(Root);
            CheckNode(newNode);

            UpdateHeightAbove(node); //更新t及其祖先(实际上只有_root一个)的高度
            return Root; //新节点必然置于树根,返回之
        }

        private void CheckNode(BinNode<T> node)
        {
            BinNode<T> temp = node;
            T rootData = temp.Element;
            while (temp.ParentNode != null)
            {
                temp = temp.ParentNode;
                if (rootData.CompareTo(temp.Element) == 0)
                {
                    int a = 0;
                }
            }
        }

        /// <summary>
        /// 删除
        /// </summary>
        public override bool Remove(T t)
        {
            if (null == Root)
            {
                return false;
            }
            BinNode<T> node = Search(t);
            //若树空或目标不存在,则无法删除
            if (null == node || node.Element.CompareTo(t) != 0)
            {
                return false;
            }
  
            BinNode<T> tempRoot = Root; //assert: 经search()后节点e已被伸展至树根
            if (!Root.HasLChild())      //若无左子树,则直接删除
            { 
                Root = Root.RightChild;
                if (null != Root)
                    Root.ParentNode = null;
            }
            else if (!Root.HasRChild())  //若无右子树,也直接删除
            { 
                Root = Root.LeftChild;
                if (null != Root)
                    Root.ParentNode = null;
            }
            else
            { //若左右子树同时存在,则
                BinNode<T> LTree = Root.LeftChild;
                LTree.ParentNode = null;
                Root.LeftChild = null; //暂时将左子树切除
                Root = Root.RightChild;
                Root.ParentNode = null; //只保留右子树
                Search(tempRoot.Element); //以原树根为目标,做一次(必定失败的)查找
                                 / assert: 至此,右子树中最小节点必伸展至根,且(因无类同节点)其左子树必空,于是
                Root.LeftChild = LTree;
                LTree.ParentNode = Root; //只需将原左子树接回原位即可
            }

            if (null != Root)
                UpdateHeight(Root); //此后,若树非空,则树根的高度需要更新
            return true; //返回成功标志
        }

        /// <summary>
        /// 将节点 node 伸展至跟
        /// </summary>
        protected BinNode<T> Splay(BinNode<T> v)
        {
            if (null == v)
            {
                return null;
            }

            // node 的父亲与祖父
            BinNode<T> p;
            BinNode<T> g;
            while ((p = v.ParentNode) != null && (g = p.ParentNode) != null) //自下而上,反复对 v 做双层伸展
            {
                BinNode<T> gg = g.ParentNode; // 每轮之后 v 都以原曾祖父(great-grand parent)为父
                if (v.IsLChild())
                {
                    if (p.IsLChild()) // zig-zig
                    {
                        AttachAsLChild(g, p.RightChild);
                        AttachAsLChild(p, v.RightChild);
                        AttachAsRChild(p, g);
                        AttachAsRChild(v, p);
                    }
                    else
                    {
                        AttachAsLChild(p, v.RightChild);
                        AttachAsRChild(g, v.LeftChild);
                        AttachAsLChild(v, g);
                        AttachAsRChild(v, p);
                    }
                }
                else if (p.IsRChild())
                {
                    AttachAsRChild(g, p.LeftChild);
                    AttachAsRChild(p, v.LeftChild);
                    AttachAsLChild(p, g);
                    AttachAsLChild(v, p);
                }
                else
                {
                    AttachAsRChild(p, v.LeftChild);
                    AttachAsLChild(g, v.RightChild);
                    AttachAsRChild(v, g);
                    AttachAsLChild(v, p);
                }

                if (null == gg)
                {
                    v.ParentNode = null;// 若 v 原先的曾祖父 gg 不存在,则v现在应为树根
                }
                else
                {
                    if (g == gg.LeftChild)
                    {
                        AttachAsLChild(gg, v);
                    }
                    else
                    {
                        AttachAsRChild(gg, v);
                    }
                }

                UpdateHeight(g);
                UpdateHeight(p);
                UpdateHeight(v);
            }//双层伸展结束时,必有g == null,但p可能非空

            p = v.ParentNode;
            if (null != p) //若p果真非空,则额外再做一次单旋
            {
                if (v.IsLChild()) {
                    AttachAsLChild(p, v.RightChild);
                    AttachAsRChild(v, p);
                }
                else {
                    AttachAsRChild(p, v.LeftChild);
                    AttachAsLChild(v, p);
                }
                UpdateHeight(p);
                UpdateHeight(v);
            }

            v.ParentNode = null;
            return v;
        }

        protected void AttachAsLChild(BinNode<T> parent, BinNode<T> lc)
        {
            parent.LeftChild = lc;
            if (null != lc)
            {
                lc.ParentNode = parent;
                //Console.WriteLine(parent.Element.ToString() + "  LeftChild:" + lc.Element.ToString());
            }
        }

        protected void AttachAsRChild(BinNode<T> parent, BinNode<T> rc)
        {
            parent.RightChild = rc;
            if (null != rc)
            {
                rc.ParentNode = parent;
                //Console.WriteLine(parent.Element.ToString() + "  RightChild:" + rc.Element.ToString());
            }
        }
    }

测试顺次添加数据:22, 10, 8, 15, 17, 20, 19, 21, 12, 13
在这里插入图片描述
查询 19 后结构如下
在这里插入图片描述
查询 8 结构如下
在这里插入图片描述

删除 19 结构如下
在这里插入图片描述

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值