作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢! 

我们讨论过,树的搜索效率与树的深度有关。二叉搜索树的深度可能为n,这种情况下,每次搜索的复杂度为n的量级。AVL树通过动态平衡树的深度,单次搜索的复杂度为log(n) (以上参考纸上谈兵 AVL树)。我们下面看伸展树(splay tree),它对于m次连续搜索操作有很好的效率。

伸展树会在一次搜索后,对树进行一些特殊的操作。这些操作的理念与AVL树有些类似,即通过旋转,来改变树节点的分布,并减小树的深度。但伸展树并没有AVL的平衡要求,任意节点的左右子树可以相差任意深度。与二叉搜索树类似,伸展树的单次搜索也可能需要n次操作。但伸展树可以保证,m次的连续搜索操作的复杂度为mlog(n)的量级,而不是mn量级。

具体来说,在查询到目标节点后,伸展树会不断进行下面三种操作中的一个,直到目标节点成为根节点 (注意,祖父节点是指父节点的父节点)

1. zig: 当目标节点是根节点的左子节点或右子节点时,进行一次单旋转,将目标节点调整到根节点的位置。

zig

2. zig-zag: 当目标节点、父节点和祖父节点成"zig-zag"构型时,进行一次双旋转,将目标节点调整到祖父节点的位置。

zig-zag

3. zig-zig:当目标节点、父节点和祖父节点成"zig-zig"构型时,进行一次zig-zig操作,将目标节点调整到祖父节点的位置。

zig-zig

单旋转操作和双旋转操作见AVL树。下面是zig-zig操作的示意图:

zig-zig operation

在伸展树中,zig-zig操作(基本上)取代了AVL树中的单旋转。通常来说,如果上面的树是失衡的,那么A、B子树很可能深度比较大。相对于单旋转(想一下单旋转的效果),zig-zig可以将A、B子树放在比较高的位置,从而减小树总的深度。

下面我们用一个具体的例子示范。我们将从树中搜索节点2:

Original

zig-zag (double rotation)

zig-zig

zig (single rotation at root)

上面的第一次查询需要n次操作。然而经过一次查询后,2节点成为了根节点,树的深度大减小。整体上看,树的大部分节点深度都减小。此后对各个节点的查询将更有效率。

伸展树的另一个好处是将最近搜索的节点放在最容易搜索的根节点的位置。在许多应用环境中,比如网络应用中,某些固定内容会被大量重复访问(比如江南style的MV)。伸展树可以让这种重复搜索以很高的效率完成。

伸展树的C实现

/* By Vamei */
/* Splay Tree */
#include <stdio.h>
#include <stdlib.h> typedef struct node *position;
typedef int ElementTP; struct node {
position parent;
ElementTP element;
position lchild;
position rchild;
}; /* pointer => root node of the tree */
typedef struct node *TREE; TREE find_value(TREE, ElementTP);
position insert_value(TREE, ElementTP); static void splay_tree(TREE, position);
static position search_value(TREE, ElementTP);
static void with_grandpa(TREE, position); static void insert_node_to_nonempty_tree(TREE, position);
static TREE left_single_rotate(TREE);
static TREE left_double_rotate(TREE);
static TREE right_single_rotate(TREE);
static TREE right_double_rotate(TREE);
static TREE left_zig_zig(TREE);
static TREE right_zig_zig(TREE); void main(void)
{
TREE tr;
tr = NULL;
tr = insert_value(tr, );
tr = insert_value(tr, );
tr = insert_value(tr, );
tr = insert_value(tr, );
tr = insert_value(tr, );
tr = insert_value(tr, ); tr = find_value(tr, );
printf("%d\n", tr->rchild->lchild->element);
} /*
* insert a value into the tree
* return root address of the tree
*/
position insert_value(TREE tr, ElementTP value)
{
position np;
/* prepare the node */
np = (position) malloc(sizeof(struct node));
np->element = value;
np->parent = NULL;
np->lchild = NULL;
np->rchild = NULL; if (tr == NULL) tr = np;
else {
insert_node_to_nonempty_tree(tr, np);
}
return tr;
} /*
*
* return NUll if not found
*/
TREE find_value(TREE tr, ElementTP value)
{
position np; np = search_value(tr, value);
if (np != NULL && np != tr) {
splay_tree(tr, np);
}
return np;
} /*
* splaying the tree after search
*/
static void splay_tree(TREE tr, position np)
{
while (tr->lchild != np && tr->rchild != np) {
with_grandpa(tr, np);
}
if (tr->lchild == np) {
right_single_rotate(tr);
}
else if (tr->rchild == np) {
left_single_rotate(tr);
}
} /*
* dealing cases with grandparent node
*/
static void with_grandpa(TREE tr, position np)
{
position parent, grandPa;
int i,j; parent = np->parent;
grandPa = parent->parent; i = (grandPa->lchild == parent) ? - : ;
j = (parent->lchild == np) ? - : ;
if (i == - && j == ) {
right_double_rotate(grandPa);
}
else if (i == && j == -) {
left_double_rotate(grandPa);
}
else if (i == - && j == -) {
right_zig_zig(grandPa);
}
else {
left_zig_zig(grandPa);
}
} /*
* search for value
*/
static position search_value(TREE tr, ElementTP value)
{
if (tr == NULL) return NULL; if (tr->element == value) {
return tr;
}
else if (value < tr->element) {
return search_value(tr->lchild, value);
}
else {
return search_value(tr->rchild, value);
}
} /*
* left single rotation
* return the new root
*/
static TREE left_single_rotate(TREE tr)
{
TREE newRoot, parent;
parent = tr->parent;
newRoot = tr->rchild;
/* detach & attach */
if (newRoot->lchild != NULL) newRoot->lchild->parent = tr;
tr->rchild = newRoot->lchild; /* raise new root node */
newRoot->lchild = tr;
newRoot->parent = parent;
if (parent != NULL) {
if (parent->lchild == tr) {
parent->lchild = newRoot;
}
else {
parent->rchild = newRoot;
}
}
tr->parent = newRoot;
return newRoot;
} /*
* right single rotation
* return the new root
*/
static TREE right_single_rotate(TREE tr)
{
TREE newRoot, parent;
parent = tr->parent;
newRoot = tr->lchild; /* detach & attach */
if (newRoot->rchild != NULL) newRoot->rchild->parent = tr;
tr->lchild = newRoot->rchild; /* raise new root node */
newRoot->rchild = tr;
newRoot->parent = parent;
if (parent != NULL) {
if (parent->lchild == tr) {
parent->lchild = newRoot;
}
else {
parent->rchild = newRoot;
}
}
tr->parent = newRoot;
return newRoot;
} /*
* left double rotation
* return
*/
static TREE left_double_rotate(TREE tr)
{
right_single_rotate(tr->rchild);
return left_single_rotate(tr);
} /*
* right double rotation
* return
*/
static TREE right_double_rotate(TREE tr)
{
left_single_rotate(tr->lchild);
return right_single_rotate(tr);
} /*
* insert a node to a non-empty tree
* called by insert_value()
*/
static void insert_node_to_nonempty_tree(TREE tr, position np)
{
/* insert the node */
if(np->element <= tr->element) {
if (tr->lchild == NULL) {
/* then tr->lchild is the proper place */
tr->lchild = np;
np->parent = tr;
return;
}
else {
insert_node_to_nonempty_tree(tr->lchild, np);
}
}
else if(np->element > tr->element) {
if (tr->rchild == NULL) {
tr->rchild = np;
np->parent = tr;
return;
}
else {
insert_node_to_nonempty_tree(tr->rchild, np);
}
}
} /*
* right zig-zig operation
*/
static TREE right_zig_zig(TREE tr)
{
position parent,middle,newRoot;
parent = tr->parent;
middle = tr->lchild;
newRoot = tr->lchild->lchild; tr->lchild = middle->rchild;
if (middle->rchild != NULL) middle->rchild->parent = tr; middle->rchild = tr;
tr->parent = middle; middle->lchild = newRoot->rchild;
if (newRoot->rchild != NULL) newRoot->rchild->parent = middle; newRoot->rchild = middle;
middle->parent = newRoot; newRoot->parent = parent;
if (parent != NULL) {
if (parent->lchild == tr) {
parent->lchild = newRoot;
}
else {
parent->rchild = newRoot;
}
}
return newRoot;
} /*
* left zig-zig operation
*/
static TREE left_zig_zig(TREE tr)
{
position parent,middle,newRoot;
parent = tr->parent;
middle = tr->rchild;
newRoot = tr->rchild->rchild; tr->rchild = middle->lchild;
if (middle->lchild != NULL) middle->lchild->parent = tr; middle->lchild = tr;
tr->parent = middle; middle->rchild = newRoot->lchild;
if (newRoot->lchild != NULL) newRoot->lchild->parent = middle; newRoot->lchild = middle;
middle->parent = newRoot; newRoot->parent = parent;
if (parent != NULL) {
if (parent->rchild == tr) {
parent->rchild = newRoot;
}
else {
parent->lchild = newRoot;
}
}
return newRoot;
}

运行结果:

4

总结

splay tree, m operations: mlog(n)

zig-zig

欢迎继续阅读“纸上谈兵: 算法与数据结构”系列。

纸上谈兵: 伸展树 (splay tree)[转]的更多相关文章

  1. K:伸展树(splay tree)

      伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...

  2. 树-伸展树(Splay Tree)

    伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二 ...

  3. 高级搜索树-伸展树(Splay Tree)

    目录 局部性 双层伸展 查找操作 插入操作 删除操作 性能分析 完整源码 与AVL树一样,伸展树(Splay Tree)也是平衡二叉搜索树的一致,伸展树无需时刻都严格保持整棵树的平衡,也不需要对基本的 ...

  4. 【BBST 之伸展树 (Splay Tree)】

    最近“hiho一下”出了平衡树专题,这周的Splay一直出现RE,应该删除操作指针没处理好,还没找出原因. 不过其他操作运行正常,尝试用它写了一道之前用set做的平衡树的题http://codefor ...

  5. 伸展树 Splay Tree

    Splay Tree 是二叉查找树的一种,它与平衡二叉树.红黑树不同的是,Splay Tree从不强制地保持自身的平衡,每当查找到某个节点n的时候,在返回节点n的同时,Splay Tree会将节点n旋 ...

  6. 伸展树(Splay tree)的基本操作与应用

    伸展树的基本操作与应用 [伸展树的基本操作] 伸展树是二叉查找树的一种改进,与二叉查找树一样,伸展树也具有有序性.即伸展树中的每一个节点 x 都满足:该节点左子树中的每一个元素都小于 x,而其右子树中 ...

  7. HDU 4453 Looploop (伸展树splay tree)

    Looploop Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  8. hdu 2871 Memory Control(伸展树splay tree)

    hdu 2871 Memory Control 题意:就是对一个区间的四种操作,NEW x,占据最左边的连续的x个单元,Free x 把x单元所占的连续区间清空 , Get x 把第x次占据的区间输出 ...

  9. 伸展树 Splay 模板

    学习Splay的时候参考了很多不同的资料,然而参考资料太杂的后果就是模板调出来一直都有问题,尤其是最后发现网上找的各种资料均有不同程度的错误. 好在啃了几天之后终于算是啃下来了. Splay也算是平衡 ...

随机推荐

  1. Scrum冲刺阶段6

    成员今日完成的任务 人员 任务 何承华 学习后端设计 陈宇 后端设计 丁培辉 学习后端设计 温志铭 信息界面设计 杨宇潇 信息界面界面设计 张主强 服务器构建学习 成员遇到的问题 人员 问题 何承华 ...

  2. elasticsearch中 refresh 和flush区别(转)

    elasticsearch中有两个比较重要的操作:refresh 和 flush refresh操作 当我们向ES发送请求的时候,我们发现es貌似可以在我们发请求的同时进行搜索.而这个实时建索引并可以 ...

  3. Ubuntu 16.04上安装Global阅读源代码工具

    参照10年前写的文档 (Linux源码阅读工具lxr和glimpse的安装与配置),想重新搭建一个源代码阅读工具,发现源里面都没有相关的工具了. 然后看到有更简单的安装工具Global可以使用,所以果 ...

  4. BZOJ3105-新Nim游戏

    Description 传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同).两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴.可以只拿一根,也可以拿走整堆火柴 ...

  5. HTML基本格式和文本元素(标签)介绍

    <!doctype html>//声明文档类型 <html lang="zh-cn">//文档开始,后面是声明是中文页面的意思,en是英语的意思 <h ...

  6. Robust Influence Maximization

    一.   研究背景 在社会和经济网络中,影响最大化问题在过去十年中得到广泛的研究,由于其广泛应用于病毒式营销[1,2],突破检测[3],谣言监测[4]等.例如公司可以通过向初始用户(称为种子)发送免费 ...

  7. java(三)数据库部分

    3.1.1.数据库的分类及常用的数据库 数据库分为:关系型数据库和非关系型数据库 关系型:mysql oracle sqlserver等 非关系型:redis,memcache,mogodb,hado ...

  8. XML学习总结二——DTD

    主要用处是约束XML. 1.DTD分为内部DTD与外部DTD两类: 内部DTD:将DTD定义在XML文档的内部 <!DOCTYPE  根元素名  [    元素描述]> 外部DTD < ...

  9. 解决nginx下connect() to 127.0.0.1:3000 failed (13: Permission denied) while connecting to upstream, client: 127.0.0.1, server: 错误信息

    尝试搭建nginx负载均衡,做域名转发时报如上错误,困扰了好长时间还好没放弃,还好百度找到解决办法: 是SeLinux导致: 1.执行下面的命令 setsebool -P httpd_can_netw ...

  10. PMP:9.项目资源管理

    项目资源管理包括识别.获取和管理所需资源以成功完成项目的各个过程,这些过程有助于确保项目经理和项目团队在正确的时间和地点使用正确的资源. 项目资源管理过程包括:   团队资源管理相对于实物资源管理,对 ...