伸展树——自顶向下

三种旋转

   当我们沿着树向下搜索某个节点X的时候,我们将搜索路径上的节点及其子树移走。我们构建两棵临时的树──左树和右树。

没有被移走的节点构成的树称作中树。在伸展操作的过程中:

1、当前节点X是中树的根。
2、左树L保存小于X的节点。
3、右树R保存大于X的节点。
开始时候,X是树T的根,左右树L和R都是空的。
1、zig:
 
                                
    如上图,在搜索到X的时候,所查找的节点比X小,将Y旋转到中树的树根。旋转之后,X及其右子树被移动到右树上。很显然,右树上的节点都大于所要查找的节点。注意X被放置在右树的最小的位置,也就是X及其子树比原先的右树中所有的节点都要小。这是由于越是在路径前面被移动到右树的节点,其值越大。
2、zig-zig:
 
                                
    在这种情况下,所查找的节点在Z的子树中,也就是,所查找的节点比X和Y都小。所以要将X,Y及其右子树都移动到右树中。首先是Y绕X右旋,然后Z绕Y右旋,最后将Z的右子树(此时Z的右子节点为Y)移动到右树中。注意右树中挂载点的位置。
3、zig-zag:
 
                            
    在这种情况中,首先将Y右旋到根。这和Zig的情况是一样的。然后变成上图右边所示的形状。接着,对Z进行左旋,将Y及其左子树移动到左树上。这样,这种情况就被分成了两个Zig情况。这样,在编程的时候就会简化,但是操作的数目增加(相当于两次Zig情况)。
    最后,在查找到节点后,将三棵树合并。如图:
 
                                

    将中树的左右子树分别连接到左树的右子树和右树的左子树上。将左右树作为X的左右子树。重新最成了一所查找的节点为根的树。

  右连接:将当前根及其右子树连接到右树上。左子结点作为新根。
  左连接:将当前根及其左子树连接到左树上。右子结点作为新根。

越是在路径前面被移动到右树的节点,其值越大;越是在路径前面移动到左树的节点,其值越小。

 这很显然,右连接,将当前根以及右子树全部连接到右树,即把整课树中值大的一部分移走(大于等于当前根),

 后面,在进行右连接,其值肯定小于之前的,所以,要加在右树的左边。

 

伸展树伪代码

 右连接:将当前根及其右子树连接到右树上。左子结点作为新根。
 左连接:将当前根及其左子树连接到左树上。右子结点作为新根。
  T : 当前的根节点。
  Function Top-Down-Splay
     Do
          If X 小于 T Then
               If X 等于 T 的左子结点 Then           //zig
                 右连接
               ElseIf X 小于 T 的左子结点 Then       //zig-zig
                 T的左子节点绕T右旋
                 右连接
               Else X大于 T 的左子结点 Then          //zig-zag
                 右连接
                 左连接
               EndIf
           ElseIf X大于 T Then
               IF X 等于 T 的右子结点 Then
                 左连接
               ElseIf X 大于 T 的右子结点 Then
                 T的右子节点绕T左旋
                 左连接
               Else X小于 T 的右子结点 Then
                 左连接
                 右连接
               EndIf
          EndIf
     While  !(找到 X或遇到空节点)
      组合左中右树
  EndFunction

zig-zag这种情形,可以先按zig处理。第二次循环在进行一次处理。简化代码如下:

  Function Top-Down-Splay
      Do
          If X 小于 T Then
               If X 小于 T 的左孩子 Then
                  T的左子节点绕T右旋
               EndIf
               右连接
              Else If X大于 T Then
                If X 大于 T 的右孩子 Then
                    T的右子节点绕T左旋
                EndIf
                左连接
              EndIf
      While  !(找到 X或遇到空节点)
      组合左中右树
  EndFuntion

例子

下面是一个查找节点19的例子:
    在例子中,树中并没有节点19,最后,距离节点最近的节点18被旋转到了根作为新的根。节点20也是距离节点19最近的节点,但是节点20没有成为新根,这和节点20在原来树中的位置有关系。
 
  如果查找的元素不在树中,则寻找过程中出现空的上一个节点即为根节点。

CPP代码

 .h

struct SplayNode
{
    int element;
    SplayNode *left;
    SplayNode *right;
};

class SplayTree
{
    public:
        SplayTree();
        ~SplayTree();

        void Insert(int data);
        void Remove(int data);
        SplayNode *Find(int data);

    private:
        SplayNode *_tree;

        SplayNode *_Insert(SplayNode *T, int item);
        SplayNode *_Remove(SplayNode *T, int item);
        SplayNode *_Splay(SplayNode *X, int Item);

        SplayNode *_SingleToRight(SplayNode *K2);
                SplayNode *_SingleToLeft(SplayNode *K2); 

                void  _MakeEmpty(SplayNode *root);

};

.cpp

SplayTree::SplayTree()
{
    _tree = NULL;
}

SplayTree::~SplayTree()
{
    _MakeEmpty(_tree);
}

void SplayTree::Insert(int data)
{
    _tree = _Insert(_tree, data);
}

void SplayTree::Remove(int data)
{
    _tree = _Remove(_tree, data);
}

SplayNode *SplayTree::_SingleToRight(SplayNode *K2)
{
    SplayNode *K1 = K2->left;
    K2->left = K1->right;
    K1->right = K2;  

    return K1;
}  

SplayNode *SplayTree::_SingleToLeft(SplayNode *K2)
{
    SplayNode *K1 = K2->right;
    K2->right = K1->left;
    K1->left  = K2;  

    return K1;
} 

SplayNode *SplayTree::_Splay(SplayNode *X, int item)
{
    static struct SplayNode Header;
    SplayNode *leftTreeMax, *rightTreeMin;

    Header.left = Header.right = NULL;
    leftTreeMax = rightTreeMin = &Header;

    while( X != NULL && item != X->element)
    {

        if(item < X->element)
        {
            if(X->left == NULL)
                break;
            if(item < X->left->element)
            {
                X = _SingleToRight(X);
            }
            if( X->left == NULL)
                break;
                        //右连接
                       &nbsp;rightTreeMin->left = X;
            rightTreeMin       = X;
            X                  = X->left;
        }
        else
        {
            if(X->right == NULL)
                break;
            if(item > X->right->element)
            {
                X = _SingleToLeft(X);
            }
            if(X->right == NULL)
                break;
                        //左连接
            &nbsp;           leftTreeMax->right = X;
            leftTreeMax        = X;
            X                  = X->right;
        }
    }
    leftTreeMax->right = X->left;
    rightTreeMin->left = X->right;
    X->left = Header.right;
    X->right = Header.left;

    return X;
}

SplayNode *SplayTree::_Insert(SplayNode *T, int item)
{
    static SplayNode* newNode = NULL;

    if(newNode == NULL)
    {
        newNode = new SplayNode;
    }
    newNode->element = item;

    if(T == NULL)
    {
        newNode->left = newNode->right = NULL;
        T = newNode;
    }
    else
    {
        T = _Splay(T, item);
        if(item < T->element)
        {
            newNode->left = T->left;
            newNode->right = T;
            T->left = NULL;
            T = newNode;
        }
        else if(T->element < item)
        {
            newNode->right = T->right;
            newNode->left = T;
            T->right = NULL;
            T = newNode;
        }
        else
        {
            delete newNode;
        }
    }
    newNode = NULL;
    return T;
}

SplayNode *SplayTree::_Remove(SplayNode *T, int item)
{
    SplayNode *newTree;

    if(T != NULL)
    {
        T = _Splay(T, item);
        if(item == T->element)
        {
            if(T->left == NULL)
            {
                newTree = T->right;
            }
            else
            {
                newTree = T->left;
                newTree = _Splay(newTree, item);
                newTree->right = T->right;
            }
            delete T;
            T = newTree;
        }
    }
    return T;
}

void SplayTree::_MakeEmpty(SplayNode *T)
{
    if(T != NULL)
    {
        _MakeEmpty(T->left);
        _MakeEmpty(T->right);
        delete T;
    }
}

SplayNode *SplayTree::Find(int data)
{
    _tree = _Splay(_tree, data);
    if(_tree->element == data)
        return _tree;
    else
        return NULL;
}

 

时间: 2024-09-20 06:09:31

伸展树——自顶向下的相关文章

Java数据结构与算法解析(八)——伸展树

伸展树简介 伸展树(Splay Tree)是特殊的二叉查找树. 它的特殊是指,它除了本身是棵二叉查找树之外,它还具备一个特点: 当某个节点被访问时,伸展树会通过旋转使该节点成为树根.这样做的好处是,下次要访问该节点时,能够迅速的访问到该节点. 特性 和普通的二叉查找树相比,具有任何情况下.任何操作的平摊O(log2n)的复杂度,时间性能上更好 和一般的平衡二叉树比如 红黑树.AVL树相比,维护更少的节点额外信息,空间性能更优,同时编程复杂度更低 在很多情况下,对于查找操作,后面的查询和之前的查询

伸展树

引用:http://digital.cs.usu.edu/~allan/DS/Notes/Ch22.pdf 一.简介:伸展树,或者叫自适应查找树,是一种用于保存有序集合的简单高效的数据结构.伸展树实质上是一个二叉查找树.允许查找,插入,删除,删除最小,删除最大,分割,合并等许多操作,这些操作的时间复杂度为O(logN).由于伸展树可以适应需求序列,因此他们的性能在实际应用中更优秀.伸展树支持所有的二叉树操作.伸展树不保证最坏情况下的时间复杂度为O(logN).伸展树的时间复杂度边界是均摊的.尽管

纸上谈兵: 伸展树 (splay tree)[转]

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!    我们讨论过,树的搜索效率与树的深度有关.二叉搜索树的深度可能为n,这种情况下,每次搜索的复杂度为n的量级.AVL树通过动态平衡树的深度,单次搜索的复杂度为log(n) (以上参考纸上谈兵 AVL树).我们下面看伸展树(splay tree),它对于m次连续搜索操作有很好的效率.   伸展树会在一次搜索后,对树进行一些特殊的操作.这些操作的理念与AVL树有些类似,即通过旋转,

6天通吃树结构—— 第四天 伸展树

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

【BBST 之伸展树 (Splay Tree)】

最近"hiho一下"出了平衡树专题,这周的Splay一直出现RE,应该删除操作指针没处理好,还没找出原因. 不过其他操作运行正常,尝试用它写了一道之前用set做的平衡树的题http://codeforces.com/problemset/problem/675/D,运行效果居然还挺好的,时间快了大概10%,内存少了大概30%. 1 #include <cstdio> 2 #include <cstring> 3 #include <string> 4

数据结构基础 伸展树

问题描述 数据结构基础 伸展树 为什么我自己画出来的展开的树和书上的不一样呢,是哪步旋转错了么? 解决方案 数据结构 - 树(基础)数据结构基础(3)-------------树第六章数据结构基础之树部分 解决方案二: 图看不清,谁知道你问的啥.

数据结构之伸展树详解_C 语言

1. 概述 二叉查找树(Binary Search Tree,也叫二叉排序树,即Binary Sort Tree)能够支持多种动态集合操作,它可以用来表示有序集合.建立索引等,因而在实际应用中,二叉排序树是一种非常重要的数据结构. 从算法复杂度角度考虑,我们知道,作用于二叉查找树上的基本操作(如查找,插入等)的时间复杂度与树的高度成正比.对一个含n个节点的完全二叉树,这些操作的最坏情况运行时间为O(log n).但如果因为频繁的删除和插入操作,导致树退化成一个n个节点的线性链(此时即为一个单链表

纸上谈兵: 伸展树 (splay tree)

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!    我们讨论过,树的搜索效率与树的深度有关.二叉搜索树的深度可能为n,这种情况下,每次搜索的复杂度为n的量级.AVL树通过动态平衡树的深度,单次搜索的复杂度为log(n) (以上参考纸上谈兵 AVL树).我们下面看伸展树(splay tree),它对于m次连续搜索操作有很好的效率.   伸展树会在一次搜索后,对树进行一些特殊的操作.这些操作的理念与AVL树有些类似,即通过旋转,

《程序设计解题策略》——第1章 利用树型数据关系的解题策略 1.1 利用划分树求解整数区间内第k大的值

第1章 利用树型数据关系的解题策略 树是一个具有层次结构的集合,一种限制前件数且没有回路的连通图.在现实生活和程序设计的竞赛试题中,许多问题的数据关系呈树型结构,因此有关树的概念.原理.操作方法和一些由树的数据结构支持的算法,一直受到编程者的重视,被广泛应用于解题过程.在本章里,我们将介绍利用树型数据关系解题的七种策略: 1) 利用划分树求解整数区间内第k大值. 2) 利用最小生成树及其扩展形式(最优比率生成树.最小k度生成树.次小生成树)计算有权连通无向图中边权和满足限制条件的最优生成树. 3