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

目录

    • 伸展树的介绍
    • 伸展树的C实现
      • 1 节点定义
      • 2 旋转
      • 3 伸展树的伸展
      • 4 搜索
      • 4 伸展树的插入和删除
    • 全部代码和参考资料

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

时间: 2024-11-08 06:20:32

伸展树 - 二叉搜索树的扩展2的相关文章

树, 二叉树, 二叉搜索树

转载:Vamei   出处:http://www.cnblogs.com/vamei 树的特征和定义 树(Tree)是元素的集合.我们先以比较直观的方式介绍树.下面的数据结构是一个树: 树有多个节点(node),用以储存元素.某些节点之间存在一定的关系,用连线表示,连线称为边(edge).边的上端节点称为父节点,下端称为子节点.树像是一个不断分叉的树根. 每个节点可以有多个子节点(children),而该节点是相应子节点的父节点(parent).比如说,3,5是6的子节点,6是3,5的父节点:1

纸上谈兵: 树, 二叉树, 二叉搜索树

树的特征和定义 树(Tree)是元素的集合.我们先以比较直观的方式介绍树.下面的数据结构是一个树: 树有多个节点(node),用以储存元素.某些节点之间存在一定的关系,用连线表示,连线称为边(edge).边的上端节点称为父节点,下端称为子节点.树像是一个不断分叉的树根. 每个节点可以有多个子节点(children),而该节点是相应子节点的父节点(parent).比如说,3,5是6的子节点,6是3,5的父节点:1,8,7是3的子节点, 3是1,8,7的父节点.树有一个没有父节点的节点,称为根节点(

二叉树、二叉搜索树、AVL树的java实现

数据结构一直都是断断续续的看,总是觉得理解的不够深入,特别是对树的理解,一直都很浅显,今儿又看了一遍,来做个总结吧. 首先,树中的一些概念: 1.树的节点包含一个数据元素,以及若干指向其子树的分支.节点拥有的子树的数量称为节点的度.节点的最大层次称为树的深度或高度. 2.二叉树是一种树形结构,其特点是每个节点至多有两棵子树,且子树有左右之分,次序不能随意颠倒. 3.满二叉树:一棵深度为k且有2^k - 1个节点的二叉树,称之为满二叉树. 4.完全二叉树:对一个深度为k,节点个数为n的二叉树,当且

数据结构——二叉搜索树、B树、B-树

数据结构——二叉搜索树.B树.B-树 1. 综述 二叉排序树(Binary Sort Tree),又叫二叉查找树(Binary Search Tree),也叫二叉排序树. 二叉搜索树满足以下性质: 1. 若根节点左子树不为空,则左子树上的所有节点均小于根节点: 2. 若根节点右子树不为空,则右子树上的所有节点均大于根节点: 3. 其左右子树也是二叉搜索树(递归定义): 4. 没有键值相等的点. B树就是B-树.B树/B-树英文叫B-Tree,可能被不小心翻译成了B-树.

平衡二叉搜索树(AVL树)的原理及实现源代码(有图文详解和C++、Java实现代码)

一.AVL树(平衡二叉搜索树)是什么? AVL树是根据它的发明者G.M. Adelson-Velsky和E.M. Landis命名的.AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树. 2.带有平衡条件:每个非叶子结点的左右子树的高度之差的绝对值(平衡因子)最多为1. 例如: 5             5 / \            /  \ 2   6         2   6 / \    \         / \ 1  4   7       1  4

算法二叉搜索树之AVL树

最近学习了二叉搜索树中的AVL树,特在此写一篇博客小结. 1.引言 对于二叉搜索树而言,其插入查找删除等性能直接和树的高度有关,因此我们发明了平衡二叉搜索树.在计算机科学中,AVL树是最先发明的自平衡二叉搜索树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.对于N个节点的AVL树,由于树高被限制为lgN,因此其插入查找删除操作耗时为O(lgN). 2.旋转 在讲解关键步骤插入与删除以前,首先我们先定义一些辅助用的操作:旋转.旋转分为左旋和右旋,其示意图如下: 相信上

数据结构第三部分:树与树的表示、二叉树及其遍历、二叉搜索树、平衡二叉树、堆、哈夫曼树、集合及其运算

参考:浙大数据结构(陈越.何钦铭)课件 1.树与树的表示 什么是树? 客观世界中许多事物存在层次关系 人类社会家谱 社会组织结构 图书信息管理 分层次组织在管理上具有更高的效率! 数据管理的基本操作之一:查找(根据某个给定关键字K,从集合R 中找出关键字与K 相同的记录).一个自然的问题就是,如何实现有效率的查找? 静态查找:集合中记录是固定的,没有插入和删除操作,只有查找 动态查找:集合中记录是动态变化的,除查找,还可能发生插入和删除 静态查找——方法一:顺序查找(时间复杂度O(n)) int

【数据结构第四周】树知识点整理(下)【二叉搜索树】

二叉搜索树 (1)定义 二叉搜索树(Binary Search Tree),也称二叉排序树或二叉查找树 一棵二叉树,可以为空:如果不为空,满足以下性质: a.非空左子树的所有键值小于其根节点的键值 b.非空右子树的所有键值大于其根节点的键值 c.左右子树都是二叉搜索树 (2)相关操作 Position Find( ElementType X, BinTree BST ):从二叉搜索树BST 中查找元素X,返回其所在结点的地址 Position FindMin( BinTree BST ):从二叉

数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树

在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉搜索树.红黑树.线索二叉树,它们在解决实际问题中有着非常重要的应用.本文主要从概念和一些基本操作上进行分类和总结. 一.概念总揽 (1) 堆 堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值