伸展树 - 二叉搜索树的扩展2

目录

1. 伸展树的介绍

伸展树(splay tree)是一种搜索二叉树,它能在 O(log n) 内完成插入、查找和删除操作。
(1)伸展树满足搜索二叉树的性质,左子节点小于根节点,右子节点大于等于根节点。
(2)伸展树独有特点:当某个节点被访问时,伸展树会通过旋转使该节点成为树的根。

伸展树的出发点是这样的:考虑到局部性原理(刚被访问的内容下次更大的可能仍会被访问)和二八原则(80%的时间只会访问20%的节点),为了使整个查找时间更小,被查找频率高的节点应当经常处于靠近根的位置。
于是提出以下方案:在每次查找之后对树进行重构,把查找到的节点移到离树根更近些。伸展树应运而生,它是一种自调整形式的二叉搜索树,它会沿着从某个节点到树根的路径,通过一系列的旋转把这个节点搬到树根上去。
因此相对“二叉搜索树”和“AVL树”,对伸展树重点关注如何旋转的

2. 伸展树的C实现

以下伸展树的实现思想来源于二叉搜索树的根插法(先插入节点到叶子,然后递归旋转到根),我们将查找到的节点旋转到根,等价于将被查找节点插入到根部:

2.1 节点定义

typedef SplayTreeNode* SplayTree;
struct SplayTreeNode {
    Item key;
    SplayTree left;
    SplayTree right;
};

伸展树不需要记录额外什么值(如AVL的高度)来维护树的信息,节省了内存。

2.2 旋转

引入两种基本的旋转:左旋和右旋
- 当被查找节点在根节点的左子树上时,以根为轴,右旋,将该节点提升到根上

//右旋--k2是根,k1是k2的左子树,将k1旋转成根 -- 以k2为原点向右旋
SplayTree rotR(SplayTree k2) 
{
    SplayTree k1 = k2->left;
    k2->left = k1->right;
    k1->right = k2;
    return k1;
}
  • 当被查找节点在根节点的右子树上时,以根为轴,左旋,将该节点提升到根上
//左旋---k2是根,k1是k2的右子树,(k1的右子树非空)将k1旋转成根 -- 以k2为原点向左旋
SplayTree rotL(SplayTree k2) 
{
    SplayTree k1 = k2->right;
    k2->right = k1->left;
    k1->left = k2;
    return k1;
}

2.3 伸展树的伸展


<注>该算法实现没有经过严格的验证,自创;
如有疑问可参考经典算法:
伸展树(一)之 图文解析 和 C语言的实现:http://www.cnblogs.com/skywang12345/p/3604238.html

设被查找节点为 x , 当查找到 x 的前驱节点时:
(1) x 在当前根的左侧,那么右旋,将和 x 接近的节点向上提升一步;
(2) x 在当前根的右侧,那么左旋,将和 x 接近的节点向上提升一步;
(3) x 的值等于当前根的值,查找结束,在上述两步递归过程中完成旋转;
前驱节点不存在时,查找结束。

递归实现过程是自底向上的,当查找节点命中后,以它的父节点为轴旋转,提升查找节点为根;向上递归,在根与查找节点的路径每步都旋转一次,直至原树根。

//伸展过程:将key对应的节点伸展到根上,并返回根节点
SplayTree Splay(SplayTree tree, Item key)
{
    if (tree == NULL)
        return tree;
    if (key == tree->key) //命中
        return tree;
    if (key < tree->key) { //左侧
        if (tree->left == NULL)
            return tree;
        tree->left = Splay(tree->left, key);
        tree = rotR(tree);
    } else { //右侧
        if (tree->right == NULL)
            return tree;
        tree->right = Splay(tree->right, key);
        tree = rotL(tree);
    }
    return tree;
}

2.4 搜索

SplayTree SplayTreeSearch(SplayTree tree, Item key)
{

    if (tree == NULL || tree->key == key)
        return tree;
    if (key < tree->key)
        return SplayTreeSearch(tree->left, key);
    else
        return SplayTreeSearch(tree->right, key);
}

2.4 伸展树的插入和删除

(1)插入:和搜索二叉树的插入相同,省略

(2)删除: 删除伸展树中键值为key的节点。
先在伸展树中查找键值为key的节点:如果没找到,则直接返回;如果找到的话则将该节点旋转成根节点,然后在删除该节点,然后将该节点的两个子树连接到一起(根节点选用和key邻近的节点);

/* 
*删除伸展树中键值为key的节点
*参数说明:
*   tree: 根节点
*   key: 待删除节点的键值
*返回:
*   根节点
*/
SplayTree SplayTreeDelete(SplayTree tree, Item key)
{
    SplayTree x = NULL;
    if (tree == NULL)
        return tree;
    tree = Splay(tree, key);
    if (tree == NULL) 
        return tree;
    if (tree->left != NULL) {
        //将根的左侧,邻近key的节点旋转成根
        x = Splay(tree->left, key);
        x->right = tree->right;
    } else {
        //tree->left == NULL
        x = tree->right;
    }
    delete tree;
    return x;
}

3. 全部代码和参考资料

#include <stdio.h>
#include <stdlib.h>
#define MAX(A, B) ((A > B) ? A : B)
typedef int Item;
typedef struct SplayTreeNode SplayTreeNode;
typedef SplayTreeNode* SplayTree;
struct SplayTreeNode {
    Item key;
    SplayTree left;
    SplayTree right;
};
static int g_error = 0; //错误代码
SplayTree NewNode(Item key, SplayTree left, SplayTree right)
{
    SplayTree x = (SplayTree)malloc(sizeof(*x));
    if (x == NULL) {
        g_error = 1;
        exit(-1);
    }
    x->key = key;
    x->left = left;
    x->right = right;
    return x;
}

SplayTree SplayTreeInit()
{
    return NewNode(10, NULL, NULL);    
}



//左旋---k2是根,k1是k2的右子树,(k1的右子树非空)将k1旋转成根 -- 以k2为原点向左旋
SplayTree rotL(SplayTree k2) 
{
    SplayTree k1 = k2->right;
    k2->right = k1->left;
    k1->left = k2;
    return k1;
}

//右旋--k2是根,k1是k2的左子树,将k1旋转成根 -- 以k2为原点向右旋
SplayTree rotR(SplayTree k2) 
{
    SplayTree k1 = k2->left;
    k2->left = k1->right;
    k1->right = k2;
    return k1;
}




SplayTree Splay(SplayTree tree, Item key)
{
    if (tree == NULL)
        return tree;
    if (key == tree->key)
        return tree;
    if (key < tree->key) { //左侧
        if (tree->left == NULL)
            return tree;
        tree->left = Splay(tree->left, key);
        tree = rotR(tree);
    } else { //右侧
        if (tree->right == NULL)
            return tree;
        tree->right = Splay(tree->right, key);
        tree = rotL(tree);
    }
    return tree;
}
SplayTree SplayTreeSearch(SplayTree tree, Item key)
{

    if (tree == NULL || tree->key == key)
        return tree;
    if (key < tree->key)
        return SplayTreeSearch(tree->left, key);
    else
        return SplayTreeSearch(tree->right, key);
}

SplayTree SplayTreeInsert(SplayTree tree, Item key)
{   
    if (tree == NULL)
        return NewNode(key, NULL, NULL);
    if(key < tree->key) 
        tree->left = SplayTreeInsert(tree->left, key);
    else
        tree->right = SplayTreeInsert(tree->right, key);
    return tree;
}

void traversal(SplayTree tree)
{
    if (tree == NULL) {
        printf("NIL\t");
        return;
    }
    printf("%d\t", tree->key);
    traversal(tree->left);
    traversal(tree->right);
    return;
}


SplayTree SplayTreeDelete(SplayTree tree, Item key)
{
    SplayTree x = NULL;
    if (tree == NULL)
        return tree;
    tree = Splay(tree, key);
    if (tree == NULL) 
        return tree;
    if (tree->left != NULL) {
        x = Splay(tree->left, key);
        x->right = tree->right;
    } else {
        //tree->left == NULL
        x = tree->right;
    }
    delete tree;
    return x;
}

int main()
{
    SplayTree splay_tree = NULL;
    //for (int i = 0; i < 10; i++) {
    //  int key = rand()%100;
    //  splay_tree = SplayTreeInsert(splay_tree, key);
    //  printf("%d\t", key);
    //}
    splay_tree = SplayTreeInsert(splay_tree, 1);
    splay_tree = SplayTreeInsert(splay_tree, 5);
    splay_tree = SplayTreeInsert(splay_tree, 4);
    splay_tree = SplayTreeInsert(splay_tree, 2);
    splay_tree = SplayTreeInsert(splay_tree, 6);

    printf("\nTraversal\n");
    traversal(splay_tree);
    splay_tree = Splay(splay_tree, 6);
    splay_tree = SplayTreeDelete(splay_tree, 4);
    printf("\nDeleted Traversal\n");
    traversal(splay_tree);
    getchar();
}

参考资料:
伸展树-维基百科:https://zh.wikipedia.org/wiki/%E4%BC%B8%E5%B1%95%E6%A0%91
伸展树(一)之 图文解析 和 C语言的实现:http://www.cnblogs.com/skywang12345/p/3604238.html
二叉搜索树的实现:http://blog.csdn.net/quzhongxin/article/details/45038399

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值