• 伸展树(二)之 C++的实现


    概要

    上一章介绍了伸展树的基本概念,并通过C语言实现了伸展树。本章是伸展树的C++实现,后续再给出Java版本。还是那句老话,它们的原理都一样,择其一了解即可。

    目录
    1. 伸展树的介绍
    2. 伸展树的C++实现(完整源码)
    3. 伸展树的C++测试程序

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3604258.html


    更多内容数据结构与算法系列 目录 

    (01) 伸展树(一)之 图文解析 和 C语言的实现
    (02) 伸展树(二)之 C++的实现
    (03) 伸展树(三)之 Java的实现

    伸展树的介绍

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

    伸展树的C++实现

    1. 基本定义
    1.1 节点

    template <class T>
    class SplayTreeNode{
        public:
            T key;                // 关键字(键值)
            SplayTreeNode *left;    // 左孩子
            SplayTreeNode *right;    // 右孩子
    
    
            SplayTreeNode():left(NULL),right(NULL) {}
    
            SplayTreeNode(T value, SplayTreeNode *l, SplayTreeNode *r):
                key(value), left(l),right(r) {}
    };

    SplayTreeNode是伸展树节点对应的类。它包括的几个组成元素:
    (01) key -- 是关键字,是用来对伸展树的节点进行排序的。
    (02) left -- 是左孩子。
    (03) right -- 是右孩子。

    1.2 伸展树

    template <class T>
    class SplayTree {
        private:
            SplayTreeNode<T> *mRoot;    // 根结点
    
        public:
            SplayTree();
            ~SplayTree();
    
            // 前序遍历"伸展树"
            void preOrder();
            // 中序遍历"伸展树"
            void inOrder();
            // 后序遍历"伸展树"
            void postOrder();
    
            // (递归实现)查找"伸展树"中键值为key的节点
            SplayTreeNode<T>* search(T key);
            // (非递归实现)查找"伸展树"中键值为key的节点
            SplayTreeNode<T>* iterativeSearch(T key);
    
            // 查找最小结点:返回最小结点的键值。
            T minimum();
            // 查找最大结点:返回最大结点的键值。
            T maximum();
    
            // 旋转key对应的节点为根节点,并返回值为根节点。
            void splay(T key);
    
            // 将结点(key为节点键值)插入到伸展树中
            void insert(T key);
    
            // 删除结点(key为节点键值)
            void remove(T key);
    
            // 销毁伸展树
            void destroy();
    
            // 打印伸展树
            void print();
        private:
    
            // 前序遍历"伸展树"
            void preOrder(SplayTreeNode<T>* tree) const;
            // 中序遍历"伸展树"
            void inOrder(SplayTreeNode<T>* tree) const;
            // 后序遍历"伸展树"
            void postOrder(SplayTreeNode<T>* tree) const;
    
            // (递归实现)查找"伸展树x"中键值为key的节点
            SplayTreeNode<T>* search(SplayTreeNode<T>* x, T key) const;
            // (非递归实现)查找"伸展树x"中键值为key的节点
            SplayTreeNode<T>* iterativeSearch(SplayTreeNode<T>* x, T key) const;
    
            // 查找最小结点:返回tree为根结点的伸展树的最小结点。
            SplayTreeNode<T>* minimum(SplayTreeNode<T>* tree);
            // 查找最大结点:返回tree为根结点的伸展树的最大结点。
            SplayTreeNode<T>* maximum(SplayTreeNode<T>* tree);
    
            // 旋转key对应的节点为根节点,并返回值为根节点。
            SplayTreeNode<T>* splay(SplayTreeNode<T>* tree, T key);
    
            // 将结点(z)插入到伸展树(tree)中
            SplayTreeNode<T>* insert(SplayTreeNode<T>* &tree, SplayTreeNode<T>* z);
    
            // 删除伸展树(tree)中的结点(键值为key),并返回被删除的结点
            SplayTreeNode<T>* remove(SplayTreeNode<T>* &tree, T key);
    
            // 销毁伸展树
            void destroy(SplayTreeNode<T>* &tree);
    
            // 打印伸展树
            void print(SplayTreeNode<T>* tree, T key, int direction);
    };

    SplayTree是伸展树对应的类。它包括根节点mRoot和伸展树的函数接口。

    2. 旋转

    旋转是伸展树中需要重点关注的,它的代码如下:

    /* 
     * 旋转key对应的节点为根节点,并返回值为根节点。
     *
     * 注意:
     *   (a):伸展树中存在"键值为key的节点"。
     *          将"键值为key的节点"旋转为根节点。
     *   (b):伸展树中不存在"键值为key的节点",并且key < tree->key。
     *      b-1 "键值为key的节点"的前驱节点存在的话,将"键值为key的节点"的前驱节点旋转为根节点。
     *      b-2 "键值为key的节点"的前驱节点存在的话,则意味着,key比树中任何键值都小,那么此时,将最小节点旋转为根节点。
     *   (c):伸展树中不存在"键值为key的节点",并且key > tree->key。
     *      c-1 "键值为key的节点"的后继节点存在的话,将"键值为key的节点"的后继节点旋转为根节点。
     *      c-2 "键值为key的节点"的后继节点不存在的话,则意味着,key比树中任何键值都大,那么此时,将最大节点旋转为根节点。
     */
    template <class T>
    SplayTreeNode<T>* SplayTree<T>::splay(SplayTreeNode<T>* tree, T key)
    {
        SplayTreeNode<T> N, *l, *r, *c;
    
        if (tree == NULL) 
            return tree;
    
        N.left = N.right = NULL;
        l = r = &N;
    
        for (;;)
        {
            if (key < tree->key)
            {
                if (tree->left == NULL)
                    break;
                if (key < tree->left->key)
                {
                    c = tree->left;                           /* rotate right */
                    tree->left = c->right;
                    c->right = tree;
                    tree = c;
                    if (tree->left == NULL) 
                        break;
                }
                r->left = tree;                               /* link right */
                r = tree;
                tree = tree->left;
            }
            else if (key > tree->key)
            {
                if (tree->right == NULL) 
                    break;
                if (key > tree->right->key) 
                {
                    c = tree->right;                          /* rotate left */
                    tree->right = c->left;
                    c->left = tree;
                    tree = c;
                    if (tree->right == NULL) 
                        break;
                }
                l->right = tree;                              /* link left */
                l = tree;
                tree = tree->right;
            }
            else
            {
                break;
            }
        }
    
        l->right = tree->left;                                /* assemble */
        r->left = tree->right;
        tree->left = N.right;
        tree->right = N.left;
    
        return tree;
    }
    
    template <class T>
    void SplayTree<T>::splay(T key)
    {
        mRoot = splay(mRoot, key);
    }

    上面的代码的作用:将"键值为key的节点"旋转为根节点,并返回根节点。它的处理情况共包括:
    (a):伸展树中存在"键值为key的节点"。
            将"键值为key的节点"旋转为根节点。
    (b):伸展树中不存在"键值为key的节点",并且key < tree->key。
            b-1) "键值为key的节点"的前驱节点存在的话,将"键值为key的节点"的前驱节点旋转为根节点。
            b-2) "键值为key的节点"的前驱节点存在的话,则意味着,key比树中任何键值都小,那么此时,将最小节点旋转为根节点。
    (c):伸展树中不存在"键值为key的节点",并且key > tree->key。
            c-1) "键值为key的节点"的后继节点存在的话,将"键值为key的节点"的后继节点旋转为根节点。
            c-2) "键值为key的节点"的后继节点不存在的话,则意味着,key比树中任何键值都大,那么此时,将最大节点旋转为根节点。

    下面列举个例子分别对a进行说明。

    在下面的伸展树中查找10,共包括"右旋" --> "右链接" --> "组合"这3步。



    (01) 右旋
    对应代码中的"rotate right"部分


    (02) 右链接
    对应代码中的"link right"部分


    (03) 组合
    对应代码中的"assemble"部分


    提示:如果在上面的伸展树中查找"70",则正好与"示例1"对称,而对应的操作则分别是"rotate left", "link left"和"assemble"。
    其它的情况,例如"查找15是b-1的情况,查找5是b-2的情况"等等,这些都比较简单,大家可以自己分析。

    3. 插入

    插入代码

    /* 
     * 将结点插入到伸展树中,并返回根节点
     *
     * 参数说明:
     *     tree 伸展树的根结点
     *     key 插入的结点的键值
     * 返回值:
     *     根节点
     */
    template <class T>
    SplayTreeNode<T>* SplayTree<T>::insert(SplayTreeNode<T>* &tree, SplayTreeNode<T>* z)
    {
        SplayTreeNode<T> *y = NULL;
        SplayTreeNode<T> *x = tree;
    
        // 查找z的插入位置
        while (x != NULL)
        {
            y = x;
            if (z->key < x->key)
                x = x->left;
            else if (z->key > x->key)
                x = x->right;
            else
            {
                cout << "不允许插入相同节点(" << z->key << ")!" << endl;
                delete z;
                return tree;
            }
        }
    
        if (y==NULL)
            tree = z;
        else if (z->key < y->key)
            y->left = z;
        else
            y->right = z;
    
        return tree;
    }
    
    template <class T>
    void SplayTree<T>::insert(T key)
    {
        SplayTreeNode<T> *z=NULL;
    
        // 如果新建结点失败,则返回。
        if ((z=new SplayTreeNode<T>(key,NULL,NULL)) == NULL)
            return ;
    
        // 插入节点
        mRoot = insert(mRoot, z);
        // 将节点(key)旋转为根节点
        mRoot = splay(mRoot, key);
    }

    insert(key)是提供给外部的接口,它的作用是新建节点(节点的键值为key),并将节点插入到伸展树中;然后,将该节点旋转为根节点。
    insert(tree, z)是内部接口,它的作用是将节点z插入到tree中。insert(tree, z)在将z插入到tree中时,仅仅只将tree当作是一棵二叉查找树,而且不允许插入相同节点。

    4. 删除

    删除代码

    /* 
     * 删除结点(节点的键值为key),返回根节点
     *
     * 参数说明:
     *     tree 伸展树的根结点
     *     key 待删除结点的键值
     * 返回值:
     *     根节点
     */
    template <class T>
    SplayTreeNode<T>* SplayTree<T>::remove(SplayTreeNode<T>* &tree, T key)
    {
        SplayTreeNode<T> *x;
    
        if (tree == NULL) 
            return NULL;
    
        // 查找键值为key的节点,找不到的话直接返回。
        if (search(tree, key) == NULL)
            return tree;
    
        // 将key对应的节点旋转为根节点。
        tree = splay(tree, key);
    
        if (tree->left != NULL)
        {
            // 将"tree的前驱节点"旋转为根节点
            x = splay(tree->left, key);
            // 移除tree节点
            x->right = tree->right;
        }
        else
            x = tree->right;
    
        delete tree;
    
        return x;
    
    }
    
    template <class T>
    void SplayTree<T>::remove(T key)
    {
        mRoot = remove(mRoot, key);
    }

    remove(key)是外部接口,remove(tree, key)是内部接口。
    remove(tree, key)的作用是:删除伸展树中键值为key的节点。
    它会先在伸展树中查找键值为key的节点。若没有找到的话,则直接返回。若找到的话,则将该节点旋转为根节点,然后再删除该节点。


    注意关于伸展树的"前序遍历"、"中序遍历"、"后序遍历"、"最大值"、"最小值"、"查找"、"打印"、"销毁"等接口与"二叉查找树"基本一样,这些操作在"二叉查找树"中已经介绍过了,这里就不再单独介绍了。当然,后文给出的伸展树的完整源码中,有给出这些API的实现代码。这些接口很简单,Please RTFSC(Read The Fucking Source Code)!

     

    伸展树的C++实现(完整源码)

    伸展树的实现文件(SplayTree.h)

      1 #ifndef _SPLAY_TREE_HPP_
      2 #define _SPLAY_TREE_HPP_
      3 
      4 #include <iomanip>
      5 #include <iostream>
      6 using namespace std;
      7 
      8 template <class T>
      9 class SplayTreeNode{
     10     public:
     11         T key;                // 关键字(键值)
     12         SplayTreeNode *left;    // 左孩子
     13         SplayTreeNode *right;    // 右孩子
     14 
     15 
     16         SplayTreeNode():left(NULL),right(NULL) {}
     17 
     18         SplayTreeNode(T value, SplayTreeNode *l, SplayTreeNode *r):
     19             key(value), left(l),right(r) {}
     20 };
     21 
     22 template <class T>
     23 class SplayTree {
     24     private:
     25         SplayTreeNode<T> *mRoot;    // 根结点
     26 
     27     public:
     28         SplayTree();
     29         ~SplayTree();
     30 
     31         // 前序遍历"伸展树"
     32         void preOrder();
     33         // 中序遍历"伸展树"
     34         void inOrder();
     35         // 后序遍历"伸展树"
     36         void postOrder();
     37 
     38         // (递归实现)查找"伸展树"中键值为key的节点
     39         SplayTreeNode<T>* search(T key);
     40         // (非递归实现)查找"伸展树"中键值为key的节点
     41         SplayTreeNode<T>* iterativeSearch(T key);
     42 
     43         // 查找最小结点:返回最小结点的键值。
     44         T minimum();
     45         // 查找最大结点:返回最大结点的键值。
     46         T maximum();
     47 
     48         // 旋转key对应的节点为根节点,并返回值为根节点。
     49         void splay(T key);
     50 
     51         // 将结点(key为节点键值)插入到伸展树中
     52         void insert(T key);
     53 
     54         // 删除结点(key为节点键值)
     55         void remove(T key);
     56 
     57         // 销毁伸展树
     58         void destroy();
     59 
     60         // 打印伸展树
     61         void print();
     62     private:
     63 
     64         // 前序遍历"伸展树"
     65         void preOrder(SplayTreeNode<T>* tree) const;
     66         // 中序遍历"伸展树"
     67         void inOrder(SplayTreeNode<T>* tree) const;
     68         // 后序遍历"伸展树"
     69         void postOrder(SplayTreeNode<T>* tree) const;
     70 
     71         // (递归实现)查找"伸展树x"中键值为key的节点
     72         SplayTreeNode<T>* search(SplayTreeNode<T>* x, T key) const;
     73         // (非递归实现)查找"伸展树x"中键值为key的节点
     74         SplayTreeNode<T>* iterativeSearch(SplayTreeNode<T>* x, T key) const;
     75 
     76         // 查找最小结点:返回tree为根结点的伸展树的最小结点。
     77         SplayTreeNode<T>* minimum(SplayTreeNode<T>* tree);
     78         // 查找最大结点:返回tree为根结点的伸展树的最大结点。
     79         SplayTreeNode<T>* maximum(SplayTreeNode<T>* tree);
     80 
     81         // 旋转key对应的节点为根节点,并返回值为根节点。
     82         SplayTreeNode<T>* splay(SplayTreeNode<T>* tree, T key);
     83 
     84         // 将结点(z)插入到伸展树(tree)中
     85         SplayTreeNode<T>* insert(SplayTreeNode<T>* &tree, SplayTreeNode<T>* z);
     86 
     87         // 删除伸展树(tree)中的结点(键值为key),并返回被删除的结点
     88         SplayTreeNode<T>* remove(SplayTreeNode<T>* &tree, T key);
     89 
     90         // 销毁伸展树
     91         void destroy(SplayTreeNode<T>* &tree);
     92 
     93         // 打印伸展树
     94         void print(SplayTreeNode<T>* tree, T key, int direction);
     95 };
     96 
     97 /* 
     98  * 构造函数
     99  */
    100 template <class T>
    101 SplayTree<T>::SplayTree():mRoot(NULL)
    102 {
    103 }
    104 
    105 /* 
    106  * 析构函数
    107  */
    108 template <class T>
    109 SplayTree<T>::~SplayTree() 
    110 {
    111     destroy(mRoot);
    112 }
    113 
    114 /*
    115  * 前序遍历"伸展树"
    116  */
    117 template <class T>
    118 void SplayTree<T>::preOrder(SplayTreeNode<T>* tree) const
    119 {
    120     if(tree != NULL)
    121     {
    122         cout<< tree->key << " " ;
    123         preOrder(tree->left);
    124         preOrder(tree->right);
    125     }
    126 }
    127 
    128 template <class T>
    129 void SplayTree<T>::preOrder() 
    130 {
    131     preOrder(mRoot);
    132 }
    133 
    134 /*
    135  * 中序遍历"伸展树"
    136  */
    137 template <class T>
    138 void SplayTree<T>::inOrder(SplayTreeNode<T>* tree) const
    139 {
    140     if(tree != NULL)
    141     {
    142         inOrder(tree->left);
    143         cout<< tree->key << " " ;
    144         inOrder(tree->right);
    145     }
    146 }
    147 
    148 template <class T>
    149 void SplayTree<T>::inOrder() 
    150 {
    151     inOrder(mRoot);
    152 }
    153 
    154 /*
    155  * 后序遍历"伸展树"
    156  */
    157 template <class T>
    158 void SplayTree<T>::postOrder(SplayTreeNode<T>* tree) const
    159 {
    160     if(tree != NULL)
    161     {
    162         postOrder(tree->left);
    163         postOrder(tree->right);
    164         cout<< tree->key << " " ;
    165     }
    166 }
    167 
    168 template <class T>
    169 void SplayTree<T>::postOrder() 
    170 {
    171     postOrder(mRoot);
    172 }
    173 
    174 /*
    175  * (递归实现)查找"伸展树x"中键值为key的节点
    176  */
    177 template <class T>
    178 SplayTreeNode<T>* SplayTree<T>::search(SplayTreeNode<T>* x, T key) const
    179 {
    180     if (x==NULL || x->key==key)
    181         return x;
    182 
    183     if (key < x->key)
    184         return search(x->left, key);
    185     else
    186         return search(x->right, key);
    187 }
    188 
    189 template <class T>
    190 SplayTreeNode<T>* SplayTree<T>::search(T key) 
    191 {
    192     return search(mRoot, key);
    193 }
    194 
    195 /*
    196  * (非递归实现)查找"伸展树x"中键值为key的节点
    197  */
    198 template <class T>
    199 SplayTreeNode<T>* SplayTree<T>::iterativeSearch(SplayTreeNode<T>* x, T key) const
    200 {
    201     while ((x!=NULL) && (x->key!=key))
    202     {
    203         if (key < x->key)
    204             x = x->left;
    205         else
    206             x = x->right;
    207     }
    208 
    209     return x;
    210 }
    211 
    212 template <class T>
    213 SplayTreeNode<T>* SplayTree<T>::iterativeSearch(T key)
    214 {
    215     return iterativeSearch(mRoot, key);
    216 }
    217 
    218 /* 
    219  * 查找最小结点:返回tree为根结点的伸展树的最小结点。
    220  */
    221 template <class T>
    222 SplayTreeNode<T>* SplayTree<T>::minimum(SplayTreeNode<T>* tree)
    223 {
    224     if (tree == NULL)
    225         return NULL;
    226 
    227     while(tree->left != NULL)
    228         tree = tree->left;
    229     return tree;
    230 }
    231 
    232 template <class T>
    233 T SplayTree<T>::minimum()
    234 {
    235     SplayTreeNode<T> *p = minimum(mRoot);
    236     if (p != NULL)
    237         return p->key;
    238 
    239     return (T)NULL;
    240 }
    241  
    242 /* 
    243  * 查找最大结点:返回tree为根结点的伸展树的最大结点。
    244  */
    245 template <class T>
    246 SplayTreeNode<T>* SplayTree<T>::maximum(SplayTreeNode<T>* tree)
    247 {
    248     if (tree == NULL)
    249         return NULL;
    250 
    251     while(tree->right != NULL)
    252         tree = tree->right;
    253     return tree;
    254 }
    255 
    256 template <class T>
    257 T SplayTree<T>::maximum()
    258 {
    259     SplayTreeNode<T> *p = maximum(mRoot);
    260     if (p != NULL)
    261         return p->key;
    262 
    263     return (T)NULL;
    264 }
    265 
    266 
    267 /* 
    268  * 旋转key对应的节点为根节点,并返回值为根节点。
    269  *
    270  * 注意:
    271  *   (a):伸展树中存在"键值为key的节点"。
    272  *          将"键值为key的节点"旋转为根节点。
    273  *   (b):伸展树中不存在"键值为key的节点",并且key < tree->key。
    274  *      b-1 "键值为key的节点"的前驱节点存在的话,将"键值为key的节点"的前驱节点旋转为根节点。
    275  *      b-2 "键值为key的节点"的前驱节点存在的话,则意味着,key比树中任何键值都小,那么此时,将最小节点旋转为根节点。
    276  *   (c):伸展树中不存在"键值为key的节点",并且key > tree->key。
    277  *      c-1 "键值为key的节点"的后继节点存在的话,将"键值为key的节点"的后继节点旋转为根节点。
    278  *      c-2 "键值为key的节点"的后继节点不存在的话,则意味着,key比树中任何键值都大,那么此时,将最大节点旋转为根节点。
    279  */
    280 template <class T>
    281 SplayTreeNode<T>* SplayTree<T>::splay(SplayTreeNode<T>* tree, T key)
    282 {
    283     SplayTreeNode<T> N, *l, *r, *c;
    284 
    285     if (tree == NULL) 
    286         return tree;
    287 
    288     N.left = N.right = NULL;
    289     l = r = &N;
    290 
    291     for (;;)
    292     {
    293         if (key < tree->key)
    294         {
    295             if (tree->left == NULL)
    296                 break;
    297             if (key < tree->left->key)
    298             {
    299                 c = tree->left;                           /* rotate right */
    300                 tree->left = c->right;
    301                 c->right = tree;
    302                 tree = c;
    303                 if (tree->left == NULL) 
    304                     break;
    305             }
    306             r->left = tree;                               /* link right */
    307             r = tree;
    308             tree = tree->left;
    309         }
    310         else if (key > tree->key)
    311         {
    312             if (tree->right == NULL) 
    313                 break;
    314             if (key > tree->right->key) 
    315             {
    316                 c = tree->right;                          /* rotate left */
    317                 tree->right = c->left;
    318                 c->left = tree;
    319                 tree = c;
    320                 if (tree->right == NULL) 
    321                     break;
    322             }
    323             l->right = tree;                              /* link left */
    324             l = tree;
    325             tree = tree->right;
    326         }
    327         else
    328         {
    329             break;
    330         }
    331     }
    332 
    333     l->right = tree->left;                                /* assemble */
    334     r->left = tree->right;
    335     tree->left = N.right;
    336     tree->right = N.left;
    337 
    338     return tree;
    339 }
    340 
    341 template <class T>
    342 void SplayTree<T>::splay(T key)
    343 {
    344     mRoot = splay(mRoot, key);
    345 }
    346 
    347 /* 
    348  * 将结点插入到伸展树中,并返回根节点
    349  *
    350  * 参数说明:
    351  *     tree 伸展树的根结点
    352  *     key 插入的结点的键值
    353  * 返回值:
    354  *     根节点
    355  */
    356 template <class T>
    357 SplayTreeNode<T>* SplayTree<T>::insert(SplayTreeNode<T>* &tree, SplayTreeNode<T>* z)
    358 {
    359     SplayTreeNode<T> *y = NULL;
    360     SplayTreeNode<T> *x = tree;
    361 
    362     // 查找z的插入位置
    363     while (x != NULL)
    364     {
    365         y = x;
    366         if (z->key < x->key)
    367             x = x->left;
    368         else if (z->key > x->key)
    369             x = x->right;
    370         else
    371         {
    372             cout << "不允许插入相同节点(" << z->key << ")!" << endl;
    373             delete z;
    374             return tree;
    375         }
    376     }
    377 
    378     if (y==NULL)
    379         tree = z;
    380     else if (z->key < y->key)
    381         y->left = z;
    382     else
    383         y->right = z;
    384 
    385     return tree;
    386 }
    387 
    388 template <class T>
    389 void SplayTree<T>::insert(T key)
    390 {
    391     SplayTreeNode<T> *z=NULL;
    392 
    393     // 如果新建结点失败,则返回。
    394     if ((z=new SplayTreeNode<T>(key,NULL,NULL)) == NULL)
    395         return ;
    396 
    397     // 插入节点
    398     mRoot = insert(mRoot, z);
    399     // 将节点(key)旋转为根节点
    400     mRoot = splay(mRoot, key);
    401 }
    402 
    403 /* 
    404  * 删除结点(节点的键值为key),返回根节点
    405  *
    406  * 参数说明:
    407  *     tree 伸展树的根结点
    408  *     key 待删除结点的键值
    409  * 返回值:
    410  *     根节点
    411  */
    412 template <class T>
    413 SplayTreeNode<T>* SplayTree<T>::remove(SplayTreeNode<T>* &tree, T key)
    414 {
    415     SplayTreeNode<T> *x;
    416 
    417     if (tree == NULL) 
    418         return NULL;
    419 
    420     // 查找键值为key的节点,找不到的话直接返回。
    421     if (search(tree, key) == NULL)
    422         return tree;
    423 
    424     // 将key对应的节点旋转为根节点。
    425     tree = splay(tree, key);
    426 
    427     if (tree->left != NULL)
    428     {
    429         // 将"tree的前驱节点"旋转为根节点
    430         x = splay(tree->left, key);
    431         // 移除tree节点
    432         x->right = tree->right;
    433     }
    434     else
    435         x = tree->right;
    436 
    437     delete tree;
    438 
    439     return x;
    440 
    441 }
    442 
    443 template <class T>
    444 void SplayTree<T>::remove(T key)
    445 {
    446     mRoot = remove(mRoot, key);
    447 }
    448 
    449 /* 
    450  * 销毁伸展树
    451  */
    452 template <class T>
    453 void SplayTree<T>::destroy(SplayTreeNode<T>* &tree)
    454 {
    455     if (tree==NULL)
    456         return ;
    457 
    458     if (tree->left != NULL)
    459         destroy(tree->left);
    460     if (tree->right != NULL)
    461         destroy(tree->right);
    462 
    463     delete tree;
    464 }
    465 
    466 template <class T>
    467 void SplayTree<T>::destroy()
    468 {
    469     destroy(mRoot);
    470 }
    471 
    472 /*
    473  * 打印"伸展树"
    474  *
    475  * key        -- 节点的键值 
    476  * direction  --  0,表示该节点是根节点;
    477  *               -1,表示该节点是它的父结点的左孩子;
    478  *                1,表示该节点是它的父结点的右孩子。
    479  */
    480 template <class T>
    481 void SplayTree<T>::print(SplayTreeNode<T>* tree, T key, int direction)
    482 {
    483     if(tree != NULL)
    484     {
    485         if(direction==0)    // tree是根节点
    486             cout << setw(2) << tree->key << " is root" << endl;
    487         else                // tree是分支节点
    488             cout << setw(2) << tree->key << " is " << setw(2) << key << "'s "  << setw(12) << (direction==1?"right child" : "left child") << endl;
    489 
    490         print(tree->left, tree->key, -1);
    491         print(tree->right,tree->key,  1);
    492     }
    493 }
    494 
    495 template <class T>
    496 void SplayTree<T>::print()
    497 {
    498     if (mRoot != NULL)
    499         print(mRoot, mRoot->key, 0);
    500 }
    501 #endif
    View Code

    伸展树的测试程序(SplayTreeTest.cpp)

     1 /**
     2  * C++ 语言: 伸展树
     3  *
     4  * @author skywang
     5  * @date 2014/02/03
     6  */
     7 
     8 #include <iostream>
     9 #include "SplayTree.h"
    10 using namespace std;
    11 
    12 static int arr[]= {10,50,40,30,20,60};
    13 #define TBL_SIZE(a) ( (sizeof(a)) / (sizeof(a[0])) )
    14 
    15 int main()
    16 {
    17     int i,ilen;
    18     SplayTree<int>* tree=new SplayTree<int>();
    19 
    20     cout << "== 依次添加: ";
    21     ilen = TBL_SIZE(arr);
    22     for(i=0; i<ilen; i++)
    23     {
    24         cout << arr[i] <<" ";
    25         tree->insert(arr[i]);
    26     }
    27 
    28     cout << "
    == 前序遍历: ";
    29     tree->preOrder();
    30 
    31     cout << "
    == 中序遍历: ";
    32     tree->inOrder();
    33 
    34     cout << "
    == 后序遍历: ";
    35     tree->postOrder();
    36     cout << endl;
    37 
    38     cout << "== 最小值: " << tree->minimum() << endl;
    39     cout << "== 最大值: " << tree->maximum() << endl;
    40     cout << "== 树的详细信息: " << endl;
    41     tree->print();
    42 
    43     i = 30;
    44     cout << "
    == 旋转节点(" << i << ")为根节点";
    45     tree->splay(i);
    46     cout << "
    == 树的详细信息: " << endl;
    47     tree->print();
    48 
    49     // 销毁二叉树
    50     tree->destroy();
    51 
    52     return 0;
    53 }
    View Code

    关于"队列的声明和实现都在头文件中"的原因,是因为队列的实现利用了C++模板,而"C++编译器不能支持对模板的分离式编译"!

    伸展树的C++测试程序

    伸展树的测试程序运行结果如下:

    == 依次添加: 10 50 40 30 20 60 
    == 前序遍历: 60 30 20 10 50 40 
    == 中序遍历: 10 20 30 40 50 60 
    == 后序遍历: 10 20 40 50 30 60 
    == 最小值: 10
    == 最大值: 60
    == 树的详细信息: 
    60 is root
    30 is 60's   left child
    20 is 30's   left child
    10 is 20's   left child
    50 is 30's  right child
    40 is 50's   left child
    
    == 旋转节点(30)为根节点
    == 树的详细信息: 
    30 is root
    20 is 30's   left child
    10 is 20's   left child
    60 is 30's  right child
    50 is 60's   left child
    40 is 50's   left child

    测试程序的主要流程是:新建伸展树,然后向伸展树中依次插入10,50,40,30,20,60。插入完毕这些数据之后,伸展树的节点是60;此时,再旋转节点,使得30成为根节点。
    依次插入10,50,40,30,20,60示意图如下:


    将30旋转为根节点的示意图如下:

  • 相关阅读:
    树的直径
    Codeforces 734E Anton and Tree(缩点+树的直径)
    Codeforces 948D Perfect Security(字典树)
    Codeforces 954D Fight Against Traffic(BFS 最短路)
    Codeforces 954C Matrix Walk (思维)
    Codeforces 950D A Leapfrog in the Array (思维)
    Codeforces 946D
    Invitation Cards POJ-1511 (spfa)
    spfa 单源最短路究极算法
    Currency Exchange POJ
  • 原文地址:https://www.cnblogs.com/skywang12345/p/3604258.html
Copyright © 2020-2023  润新知