伸展树Splay Tree

这里写图片描述

伸展树

伸展树是自平衡的二叉查找树,伸展树不像AVL树,要时刻保持平衡。伸展树通过分摊保持查找的高效率。
伸展树的基本思想:当一个节点被访问后,它就要进过一系列AVL树旋转被放在根上。

数据局部性

这里写图片描述

可视化伸展树

http://www.link.cs.cmu.edu/splay/
这个网站是伸展树的创始人做到一个project。将伸展树可视化。学伸展树的时候可以看看。

实现

由于数据结构与算法分析:C语言描述上面只是介绍了伸展树,但是没有代码实现。所以这篇blog主要写我是怎么实现伸展树的。

zig/zag

伸展树里最重要的一点就是zig/zag

zig

zig其实就是AVL的左旋
这里写图片描述

zag

zag其实就是AVL的右旋
这里写图片描述

zig/zag组合

有了zig,zag。就可以实现伸展树了。
伸展树通过zig,zag的组合来旋转。伸展式是通过两层旋转。为什么可以可见书上证明。

zig,zig组合

如果V是父节点的左儿子,V的父节点是V祖父节点的左儿子。如图:
这里写图片描述
这个时候要用zig,zig旋转,如图
这里写图片描述

zag,zag组合

如果V是父节点的右儿子,V的父节点是V祖父节点的右儿子。如图:
这里写图片描述
这个时候要用zag,zag旋转,如图
这里写图片描述

zig,zag组合

如果V是父节点的左儿子,V的父节点是V祖父节点的右儿子。如图:
这里写图片描述
这个时候要用zig,zag旋转,如图
这里写图片描述

zag,zig组合

如果V是父节点的右儿子,V的父节点是V祖父节点的左儿子。如图:
这里写图片描述
这个时候要用zig,zag旋转,如图
这里写图片描述

code:

上面的图终于搞完了。上代码:我没有使用书最后一章自顶而下的实现方法。而是自下而上。所以每个节点要附带父亲节点。所以这种实现方法只供学习练习。要真正使用还是实现自顶而下的伸展树。
Node class的定义:

class Node
{
    friend class SplayTree;
public:
    Node(int V) :val(V), left(nullptr), right(nullptr),parent(nullptr) { ; }
    ~Node()=default;
private:
    int val;
    Node *left, *right;
    Node *parent;
};

由于其他操作和BST是一样的,所以我只给出搜索的代码:
搜索函数的基本框架就如下所示:

class SplayTree
{
public:
    Node* search(int x) {
        _root=_Find(_root, x);
        return _root;
    }
    int Retrieve(Node* root) {
        return root ==nullptr?-1:root->val;
private:
    Node* _Find(Node *root,int x);
    Node* splay(Node *root);
    Node* zip(Node *);
    Node* zap(Node *);
    Node * _root;
};

_Find函数:找到该寻找的节点,并调用splay函数。

Node * SplayTree::_Find(Node *root,int x)
{
    if (root == nullptr)
        return nullptr;
    if (x < root->val)
        return _Find(root->left, x);
    else if (x > root->val)
        return _Find(root->right, x);
    else
        root = splay(root);
    return root;
}

splay函数,将该节点旋转至root.
Node* f是父亲节点,g是祖父节点

Node * SplayTree::splay(Node *root)
{
    Node* f, *g;
    while ((f=root->parent) && (g=f->parent)) {     //root有父节点和祖父节点          g
        Node* gg = g->parent;                                   //               f
        if (g->left == f) {                                  //             root   
            if (f->left == root) {    
                f = zip(g);
                root = zip(f);
            }       
            else {                  //      g
                g->left=zap(f);     //    f
                root = zip(g);      //     root
            }                       
        }
        else {
            if (f->left == root) {           //  g
                g->right = zip(f);          //    f
                root = zap(g);             // root
            }                       
            else {                 //     g
                f = zip(g);       //        f
                root = zip(f);    //         root
            }                    
        }
        if (gg == nullptr)        //到树根了
            root->parent = nullptr;
        else if (g == gg->left)
            gg->left = root;
        else
            gg->right = root;
    }
    if (f!=nullptr) {
        if (f->left == root) {
            root = zip(f);
        }
        else if(f->right == root){
            root = zap(f);
        }
    }
    root->parent = nullptr;
    return root;
}

下面是zig和zap函数的实现,其实就是AVL里的左旋右旋差不多。

Node * SplayTree::zip(Node *root)              //     root
{                                             //    v
    Node* v = root->left;                   //   
    Node* parent = root->parent;
    root->left = v->right;
    v->right = root;
    if(root->left !=nullptr)
        root->left->parent = root;
    root->parent = v;
    v->parent = parent;
    return v;
}

Node * SplayTree::zap(Node *root)          //  root
{                                         //     v
    Node* v = root->right;
    Node* parent = root->parent;
    root->right = v->left;
    v->left = root;
    if(root->right !=nullptr)
        root->right->parent = root;
    root->parent = v;
    v->parent = parent;
    return v;
}

完结撒花d=====( ̄▽ ̄*)b

这里写图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41256413/article/details/82156214