`

Splay tree --扩展树

阅读更多

扩展树的C语言实现版本,这个是自上而下且节点带大小(size)的扩展树(伸展树)的具体实现。它由Daniel Dominic Sleator 和Robert Endre Tarjan,Tarjan在计算机算法领域,是个大师级别的人物,求强连通图的分量scc算法,很多基础的算法都是他发现的一个扩展树是自适应调整的二叉搜索树。其他常见的二叉搜索树有,AVL tree,red-black tree(红黑树)。但这里有个明显的区别,splay tree 通过节点额外附带属性信息,该信息记录最近访问的节点。它提供基本操作,如插入,查询和删除,平摊情况下都是 O(logN)的渐近复杂度,单次操作,要比,要对于非随机访问操作,splay tree,即使在不知道访问序列的具体模式情况下,也比其他搜索树提供较好性能。二叉搜索树上的所有普通操作(节点的增删改查),如果整合一个基本操作,都成为splaying,按照一定元素顺序Splaying 这个树,即重新排列了节点,并且这个节点被置为根。一种方法是,第一步,基于标准二叉树的执行对该节点原有的操作,然后基于树的旋转,使得这个节点成为根节点。相应的,一个top-down算法可以整合查询和树的重新组织为一个单一的步骤。

 

头文件:

#ifndef _SPLAY_TREE_H_
#define _SPLAY_TREE_H_

typedef struct tree_node {
    struct tree_node * left, * right;
    int key;
    int size;   /* maintained to be the number of nodes rooted here */

    void *data;
} splay_tree;


splay_tree * splaytree_splay (splay_tree *t, int key);
splay_tree * splaytree_insert(splay_tree *t, int key, void *data);
splay_tree * splaytree_delete(splay_tree *t, int key);
splay_tree * splaytree_size(splay_tree *t);

#define splaytree_size(x) (((x)==NULL) ? 0 : ((x)->size))
/* This macro returns the size of a node.  Unlike "x->size",     */
/* it works even if x=NULL.  The test could be avoided by using  */
/* a special version of NULL which was a real node with size 0.  */


#endif

 

实现代码:

/*
           An implementation of top-down splaying with sizes
             D. Sleator <sleator@cs.cmu.edu>, January 1994.

  This extends top-down-splay.c to maintain a size field in each node.
  This is the number of nodes in the subtree rooted there.  This makes
  it possible to efficiently compute the rank of a key.  (The rank is
  the number of nodes to the left of the given key.)  It it also
  possible to quickly find the node of a given rank.  Both of these
  operations are illustrated in the code below.  The remainder of this
  introduction is taken from top-down-splay.c.

  "Splay trees", or "self-adjusting search trees" are a simple and
  efficient data structure for storing an ordered set.  The data
  structure consists of a binary tree, with no additional fields.  It
  allows searching, insertion, deletion, deletemin, deletemax,
  splitting, joining, and many other operations, all with amortized
  logarithmic performance.  Since the trees adapt to the sequence of
  requests, their performance on real access patterns is typically even
  better.  Splay trees are described in a number of texts and papers
  [1,2,3,4].

  The code here is adapted from simple top-down splay, at the bottom of
  page 669 of [2].  It can be obtained via anonymous ftp from
  spade.pc.cs.cmu.edu in directory /usr/sleator/public.

  The chief modification here is that the splay operation works even if the
  item being splayed is not in the tree, and even if the tree root of the
  tree is NULL.  So the line:

                              t = splay(i, t);

  causes it to search for item with key i in the tree rooted at t.  If it's
  there, it is splayed to the root.  If it isn't there, then the node put
  at the root is the last one before NULL that would have been reached in a
  normal binary search for i.  (It's a neighbor of i in the tree.)  This
  allows many other operations to be easily implemented, as shown below.

  [1] "Data Structures and Their Algorithms", Lewis and Denenberg,
       Harper Collins, 1991, pp 243-251.
  [2] "Self-adjusting Binary Search Trees" Sleator and Tarjan,
       JACM Volume 32, No 3, July 1985, pp 652-686.
  [3] "Data Structure and Algorithm Analysis", Mark Weiss,
       Benjamin Cummins, 1992, pp 119-130.
  [4] "Data Structures, Algorithms, and Performance", Derick Wood,
       Addison-Wesley, 1993, pp 367-375
*/

#include "splaytree.h"
#include <stdlib.h>
#include <assert.h>

#define compare(i,j) ((i)-(j))
/* This is the comparison.                                       */
/* Returns <0 if i<j, =0 if i=j, and >0 if i>j                   */

#define node_size splaytree_size

/* Splay using the key i (which may or may not be in the tree.)
 * The starting root is t, and the tree used is defined by rat
 * size fields are maintained */
splay_tree * splaytree_splay (splay_tree *t, int i) {
    splay_tree N, *l, *r, *y;
    int comp, l_size, r_size;

    if (t == NULL) return t;
    N.left = N.right = NULL;
    l = r = &N;
    l_size = r_size = 0;
/*查找key,一边查找一边进行旋转操作,通过compare函数,这个在c++中需要重载-号*/
    for (;;) {
        comp = compare(i, t->key);
        if (comp < 0) {
            if (t->left == NULL) break;
            if (compare(i, t->left->key) < 0) {/*如果要查询的值比节点值小,且比左子节点小,LL类型*/
                y = t->left;                           /* rotate right ,LL类型,右旋*/
                t->left = y->right;
                y->right = t;
                t->size = node_size(t->left) + node_size(t->right) + 1;
                t = y;
                if (t->left == NULL) break;
            }
            r->left = t;                               /* link right */
            r = t;
            t = t->left;
            r_size += 1+node_size(r->right);
        } else if (comp > 0) {
            if (t->right == NULL) break;
            if (compare(i, t->right->key) > 0) {
                y = t->right;                          /* rotate left */
                t->right = y->left;
                y->left = t;
		t->size = node_size(t->left) + node_size(t->right) + 1;
                t = y;
                if (t->right == NULL) break;
            }
            l->right = t;                              /* link left */
            l = t;
            t = t->right;
            l_size += 1+node_size(l->left);
        } else {
            break;
        }
    }
    l_size += node_size(t->left);  /* Now l_size and r_size are the sizes of */
    r_size += node_size(t->right); /* the left and right trees we just built.*/
    t->size = l_size + r_size + 1;

    l->right = r->left = NULL;

    /* The following two loops correct the size fields of the right path  */
    /* from the left child of the root and the right path from the left   */
    /* child of the root.                                                 */
    for (y = N.right; y != NULL; y = y->right) {
        y->size = l_size;
        l_size -= 1+node_size(y->left);
    }
    for (y = N.left; y != NULL; y = y->left) {
        y->size = r_size;
        r_size -= 1+node_size(y->right);
    }

    l->right = t->left;                                /* assemble */
    r->left = t->right;
    t->left = N.right;
    t->right = N.left;

    return t;
}

splay_tree * splaytree_insert(splay_tree * t, int i, void *data) {
/* Insert key i into the tree t, if it is not already there. */
/* Return a pointer to the resulting tree.                   */
    splay_tree * new;

    if (t != NULL) {
	t = splaytree_splay(t, i);
	if (compare(i, t->key)==0) {
	    return t;  /* it's already there */
	}
    }
    new = (splay_tree *) malloc (sizeof (splay_tree));
    assert(new);
    if (t == NULL) {
	new->left = new->right = NULL;
    } else if (compare(i, t->key) < 0) {
	new->left = t->left;
	new->right = t;
	t->left = NULL;
	t->size = 1+node_size(t->right);
    } else {
	new->right = t->right;
	new->left = t;
	t->right = NULL;
	t->size = 1+node_size(t->left);
    }
    new->key = i;
    new->data = data;
    new->size = 1 + node_size(new->left) + node_size(new->right);
    return new;
}

splay_tree * splaytree_delete(splay_tree *t, int i) {
/* Deletes i from the tree if it's there.               */
/* Return a pointer to the resulting tree.              */
    splay_tree * x;
    int tsize;

    if (t==NULL) return NULL;
    tsize = t->size;
    t = splaytree_splay(t, i);
    if (compare(i, t->key) == 0) {               /* found it */
	if (t->left == NULL) {
	    x = t->right;
	} else {
	    x = splaytree_splay(t->left, i);
	    x->right = t->right;
	}
	free(t);
	if (x != NULL) {
	    x->size = tsize-1;
	}
	return x;
    } else {
	return t;                         /* It wasn't there */
    }
}

#if 0
static splay_tree *find_rank(int r, splay_tree *t) {
/* Returns a pointer to the node in the tree with the given rank.  */
/* Returns NULL if there is no such node.                          */
/* Does not change the tree.  To guarantee logarithmic behavior,   */
/* the node found here should be splayed to the root.              */
    int lsize;
    if ((r < 0) || (r >= node_size(t))) return NULL;
    for (;;) {
	lsize = node_size(t->left);
	if (r < lsize) {
	    t = t->left;
	} else if (r > lsize) {
	    r = r - lsize -1;
	    t = t->right;
	} else {
	    return t;
	}
    }
}
#endif

 

 

splaytree算法复杂度:http://en.wikipedia.org/wiki/Splay_tree

平摊分析情况下:

 

Splay tree Type Invented Invented by Time complexity
in big O notation Space Search Insert Delete
Tree
1985
Daniel Dominic Sleator and Robert Endre Tarjan
Average Worst case
O(n) O(n)
O(log n) amortized O(log n)
O(log n) amortized O(log n)
O(log n) amortized O(log n)

 

 

分享到:
评论

相关推荐

    splay-tree:快速的八叉树实现为实习录入任务

    虽然这里提到的是八叉树,但splay的概念可以扩展到多叉树。它的核心思想是每次访问或操作一个节点时,通过一系列旋转操作将该节点移动到根位置,从而使得频繁访问的节点在树中的位置更靠近根,减少了后续查找的平均...

    splay_tree:Rust的基于Splay树的集合(例如,地图,集合,堆)库

    splay_tree splay_tree提供基于自上而下的splay树的数据结构,例如map,set和heap。 扩展树是一种自我调整的二进制搜索树,具有最近访问的元素可以快速再次访问的附加属性。 它在O(log n)摊销时间内执行基本操作,...

    Splay.pdf【算法与数据结构】

    - **定义**:二叉搜索树(Binary Search Tree,简称BST)是一种特殊的二叉树,其中每个节点包含一个键(或关键字)和两个子树(左子树和右子树)。对于任意节点来说,其左子树中的所有节点的键都小于该节点的键;其...

    splay_tree_map.cr:这是Splay Tree的Crystal实现。 这是一种半平衡的二叉搜索树,易于自我优化,因此访问最多的项最快。

    此实现提供了类似哈希的界面,并且提供了Splay树中通常不存在的几个功能-有效删除通常访问最少的项,并提供额外的快速搜索选项。叶修剪由于八字树倾向于将自身与最常访问的元素一起组织到树的根部,因此最不频繁...

    rope-utf16-splay:针对使用UTF-16代码单元和行列对进行索引和更新而优化的粗字符串

    **展开树(Splay Tree)** 展开树是一种自调整的二叉搜索树。每当访问一个节点时,它会被移动到树的根部,以减少未来对这个节点的访问时间。相比于传统手指树(Finger Tree),展开树在某些操作上可能具有更快的...

    第8章-高级搜索树1

    在本章节中,我们将探讨高级搜索树,特别是关于伸展树(Splay Tree)的一些高级特性。伸展树是一种自调整的二叉查找树,它通过旋转操作来改善最近访问的节点的访问效率。题目[8-1]和[8-2]分别关注了伸展树在处理相等...

    C++ STL PBDS库讲解

    例如,用户可以选择不同的底层实现,如红黑树(rb_tree_tag)、伸展树(splay_tree_tag)或有序向量树(ov_tree_tag)。通常红黑树提供了较好的平衡性能,所以在总体性能上,使用rb_tree_tag可能更优。 下面是一个...

    杭电AcmKeHaoWanLe模板1

    - **Splay Tree**:自适应搜索树,每次访问节点后将其移动到根部,以优化后续访问。分为普通Splay和缩点Splay,后者处理路径压缩场景。 - **Treap**:结合随机性的平衡二叉搜索树,每个节点附带有随机优先级,保证...

    acm-template:acm-icpc的一些模板

    splay link-cut tree 可持久化treap AC自动机 树链剖分 树的点分治 树的边分治 图论 图的基本结构 强联通分量 无向图求桥 无向图求割点 二分图匹配 匈牙利算法 Hopcroft-Karp算法 二分图最优匹配 KM 算法 最小树形图...

    IOI国家集训队论文集1999-2019

    + [Splay](#splay) * [图论](#图论) + [图论](#图论-1) + [模型建立](#模型建立) + [网络流](#网络流) + [最短路](#最短路) + [欧拉路](#欧拉路) + [差分约束系统](#差分约束系统) + [平面图](#平面图) + ...

    ACM_算法模板集.pdf

    - **伸展树 (Splay Tree)** - 定义:一种自调整的二叉搜索树。 - **Treap (Randomized Binary Search Tree)** - 定义:结合了二叉搜索树和堆性质的数据结构。 - **0/1分数规划K约束 (0/1 Fractional Programming ...

    九野的模版3.15.10.pdf

    - **Splay Tree (伸展树)**:一种自平衡的二叉搜索树,通过对频繁访问的节点进行伸展操作以优化性能。 - **Link-Cut Tree (链剖分树)**:一种特殊的二叉搜索树,用于高效地处理树上的动态操作,如切割、连接等。 ##...

    acm学习用的书

    - 常用的数据结构,如并查集、树状数组、线段树、字典树、Splay树、ST表、划分树、树链剖分、Link-Cut Tree等。 - 字符串处理:包括后缀数组、KMP算法、AC自动机、最长公共子串、最长回文子串等。 四、图论与算法 -...

    C++的pb_ds库在OI中的应用集合

    - **Tree-based Containers**: 如`cc_hash_table`, `rb_tree`, `splay_tree`等,它们提供了类似STL中的`set`和`map`的功能,但往往在特定操作上有着更好的性能。 - **Hash-based Containers**: 如`hash_set`, `hash_...

    ACM编程所用资料

    - 树状数据结构如并查集、树状数组、线段树、字典树、Splay树、ST表等。 - 高级数据结构如Link-Cut Tree、树链剖分等。 5. STL使用技巧 - STL容器的使用,如list、set、map。 - STL算法的使用和理解。 6. 语言...

    Insane-Bot Client-开源

    3. **SplayTree.cls** - Splay Tree是一种自平衡的二叉查找树,常用于快速查找和更新操作。在这个插件中,SplayTree可能被用来优化项目的存储和检索,确保在大量banes请求时保持高效性能。 4. **GameObj.cls** - 这...

    PBDS 库.pdf

    在PBDS库中,提供了多种数据结构的实现,包括但不限于红黑树(rb_tree_tag)、伸展树(splay_tree_tag)和有序向量树(ov_tree_tag)。开发者可以根据应用场景选择最合适的数据结构,从而达到优化性能的目的。在描述...

    数据结构课程设计--平衡二叉树的动态演示

    平衡二叉树(Balanced Binary Tree)是一种特殊的二叉树结构,它保持了树的高度平衡,从而确保了数据的查找、插入和删除操作具有较高的效率。在本课程设计中,我们将深入探讨平衡二叉树的原理及其动态演示,特别是它...

Global site tag (gtag.js) - Google Analytics