伍佰目录 短网址
  当前位置:海洋目录网 » 站长资讯 » 教育考试 » 文章详细 订阅RssFeed

数据结构与算法专题——第十题 输入法跳不过的坎-伸展树

来源:本站原创 浏览:93次 时间:2022-12-29

我们知道AVL树为了保持严格的平衡,所以在数据插入上会呈现过多的旋转,影响了插入和删除的性能,此时AVL的一个变种伸展树(Splay)就应运而生了,我们知道万事万物都遵循一个“八二原则“,也就是说80%的人只会用到20%的数据,比如说我们的“QQ输入法”,平常打的字也就那么多,或许还没有20%呢。

一:伸展树1:思想

伸展树的原理就是这样的一个”八二原则”,比如我要查询树中的“节点7”,如果我们是AVL的思路,每次都查询“节点7”,那么当这棵树中的节点越来越多的情况下就会呈现下旋,所以复杂度只会递增,伸展树的想法就是在第一次查询时树里面会经过一阵痉挛把“节点7”顶成“根节点”,操作类似AVL的双旋转,比如下图:

当我们再次查询同样的”数字7“时,直接在根节点处O(1)取出,当然这算是一个最理想的情况,有时痉挛过度,会出现糟糕的”链表“,也就退化了到O(N),所以伸展树讲究的是”摊还时间“,意思就是说在”连续的一系列操作中的平均时间“,当然可以保证是log(N)。

2:伸展方式

不知道大家可否记得,在AVL中的旋转要分4个情况,同样伸展树中的伸展需要考虑6种情况,当然不考虑镜像的话也就是3种情况,从树的伸展方向上来说有“自下而上”和“自上而下"的两种方式,考虑到代码实现简洁,我还是说下后者。

1) 自上而下的伸展

这种伸展方式会把树切成三份,L树,M树,R树,考虑的情况有:单旋转,“一字型”旋转,“之字形”旋转。

  • 单旋转

从图中我们可以看到,要将“节点2”插入到根上,需要将接近于“节点2”的数插入到根上,也就是这里的“节点7”,首先树被分成了3份,初始情况,L和R树是“空节点”,M是整棵树,现在需要我们一步一步拆分,当我们将“节点2”试插入到“节点7”的左孩子时,发现“节点7”就是父节点,满足“单旋转”情况,然后我们将整棵树放到“R树”中的left节点上,M此时是一个逻辑上的空节点,然后我们将R树追加到M树中。L树追加到M的左子树中,最后我们将“节点2”插入到根节点上。说这么多有点拗口,伸展树比较难懂,需要大家仔细品味一下。

  • 一字型

一字型旋转方式与我们AVL中的“单旋转”类似,首先同样我们切成了三份,当我们"预插入20时”,发现20的“父节点”是根的右孩子,而我们要插入的数字又在父节点的右边,此时满足”一字型“旋转,我们将7,10两个节点按照”右右情况”旋转,旋转后“节点10"的左孩子放入到L树的right节点,"节点10”作为中间树M,最后将20插入根节点。

  • 之字形

之字形有点类似AVL中的“双旋转”,不过人家采取的策略是不一样的,当我们试插入“节点9”,同样发现“父节点”是根的右儿子,并且“节点9”要插入到父节点的内侧,根据规则,需要将“父节点10”作为M树中的根节点,“节点7”作为L树中的right节点,然后M拼接L和R,最后将节点9插入到根上。

3:基本操作1) 节点定义

我们还是采用普通二叉树中的节点定义,也就没有了AVL那么烦人的高度信息。


public class BinaryNode<T>
    {
        // Constructors
        public BinaryNode(T theElement) : this(theElement, null, null) { }

        public BinaryNode(T theElement, BinaryNode<T> lt, BinaryNode<T> rt)
        {
            element = theElement;
            left = lt;
            right = rt;
        }

        public T element;

        public BinaryNode<T> left;

        public BinaryNode<T> right;
    }

2) 伸展

这里为了编写代码方便,我采用的是逻辑nullNode节点,具体伸展逻辑大家可以看上面的图。


#region 伸展
        /// <summary>
        /// 伸展
        /// </summary>
        /// <param name="Key"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        public BinaryNode<T> Splay(T Key, BinaryNode<T> tree)
        {
            BinaryNode<T> leftTreeMax, rightTreeMin;

            header.left = header.right = nullNode;

            leftTreeMax = rightTreeMin = header;

            nullNode.element = Key;

            while (true)
            {
                int compareResult = Key.CompareTo(tree.element);

                if (compareResult < 0)
                {
                    //如果成立,说明是”一字型“旋转
                    if (Key.CompareTo(tree.left.element) < 0)
                        tree = rotateWithLeftChild(tree);

                    if (tree.left == nullNode)
                        break;

                    //动态的将中间树的”当前节点“追加到 R 树中,同时备份在header中
                    rightTreeMin.left = tree;

                    rightTreeMin = tree;

                    tree = tree.left;
                }
                else if (compareResult > 0)
                {
                    //如果成立,说明是”一字型“旋转
                    if (Key.CompareTo(tree.right.element) > 0)
                        tree = rotateWithRightChild(tree);

                    if (tree.right == nullNode)
                        break;

                    //动态的将中间树的”当前节点“追加到 L 树中,同时备份在header中
                    leftTreeMax.right = tree;

                    leftTreeMax = tree;

                    tree = tree.right;
                }
                else
                {
                    break;
                }
            }

            /* 剥到最后一层,来最后一次切分 */
            //把中间树的左孩子给“左树”
            leftTreeMax.right = tree.left;

            //把中间树的右孩子给“右树”
            rightTreeMin.left = tree.right;

            /* 合并操作 */
            //将头节点的左树作为中间树的左孩子
            tree.left = header.right;

            //将头结点的右树作为中间树的右孩子
            tree.right = header.left;

            return tree;
        }
        #endregion

3) 插入

插入操作关键在于我们要找到接近于”要插入点“的节点,然后顶成“根节点”,也就是上面三分图中的最后一分。


#region 插入
        /// <summary>
        /// 插入
        /// </summary>
        /// <param name="Key"></param>
        public void Insert(T Key)
        {
            if (newNode == null)
                newNode = new BinaryNode<T>(default(T));

            newNode.element = Key;

            if (root == nullNode)
            {
                newNode.left = newNode.right = nullNode;

                root = newNode;
            }
            else
            {
                root = Splay(Key, root);

                int compareResult = Key.CompareTo(root.element);

                if (compareResult < 0)
                {
                    newNode.left = root.left;

                    newNode.right = root;

                    root.left = nullNode;

                    root = newNode;
                }
                else
                    if (compareResult > 0)
                    {
                        newNode.right = root.right;

                        newNode.left = root;

                        root.right = nullNode;

                        root = newNode;
                    }
                    else
                        return;
            }

            newNode = null;
        }
        #endregion

4) 删除

删除操作也要将节点伸展到根上,然后进行删除,逻辑很简单。


#region 删除
        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="Key"></param>
        public void Remove(T Key)
        {
            BinaryNode<T> newTree;

            //将删除结点顶到根节点
            root = Splay(Key, root);

            //不等于说明没有找到
            if (root.element.CompareTo(Key) != 0)
                return;

            //如果左边为空,则直接用root的右孩子接上去
            if (root.left == nullNode)
            {
                newTree = root.right;
            }
            else
            {
                newTree = root.left;

                newTree = Splay(Key, newTree);

                newTree.right = root.right;
            }
            root = newTree;
        }
        #endregion

伸展树可以总结成一幅图:


  推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • 就要爱网站目录就要爱网站目录

    就要爱网站目录,按主题和类别列出网站。所有提交的网站都经过人工审查,确保质量和无垃圾邮件的结果。

    www.912219.com
  • 伍佰目录伍佰目录

    伍佰网站目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向伍佰目录推荐、提交优秀网站。

    www.wbwb.net