纸上谈兵: 伸展树 (splay tree)[转]
- 作者: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)[转]的更多相关文章
- K:伸展树(splay tree)
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...
- 树-伸展树(Splay Tree)
伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二 ...
- 高级搜索树-伸展树(Splay Tree)
目录 局部性 双层伸展 查找操作 插入操作 删除操作 性能分析 完整源码 与AVL树一样,伸展树(Splay Tree)也是平衡二叉搜索树的一致,伸展树无需时刻都严格保持整棵树的平衡,也不需要对基本的 ...
- 【BBST 之伸展树 (Splay Tree)】
最近“hiho一下”出了平衡树专题,这周的Splay一直出现RE,应该删除操作指针没处理好,还没找出原因. 不过其他操作运行正常,尝试用它写了一道之前用set做的平衡树的题http://codefor ...
- 伸展树 Splay Tree
Splay Tree 是二叉查找树的一种,它与平衡二叉树.红黑树不同的是,Splay Tree从不强制地保持自身的平衡,每当查找到某个节点n的时候,在返回节点n的同时,Splay Tree会将节点n旋 ...
- 伸展树(Splay tree)的基本操作与应用
伸展树的基本操作与应用 [伸展树的基本操作] 伸展树是二叉查找树的一种改进,与二叉查找树一样,伸展树也具有有序性.即伸展树中的每一个节点 x 都满足:该节点左子树中的每一个元素都小于 x,而其右子树中 ...
- HDU 4453 Looploop (伸展树splay tree)
Looploop Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- hdu 2871 Memory Control(伸展树splay tree)
hdu 2871 Memory Control 题意:就是对一个区间的四种操作,NEW x,占据最左边的连续的x个单元,Free x 把x单元所占的连续区间清空 , Get x 把第x次占据的区间输出 ...
- 伸展树 Splay 模板
学习Splay的时候参考了很多不同的资料,然而参考资料太杂的后果就是模板调出来一直都有问题,尤其是最后发现网上找的各种资料均有不同程度的错误. 好在啃了几天之后终于算是啃下来了. Splay也算是平衡 ...
随机推荐
- mac系统 pip3 install scrapy 失败 No local packages or working download links found for incremental>=16.10.1
使用pip3 install scrapy命令之后,会出现如下问题: Collecting scrapy Downloading Scrapy-1.4.0-py2.py3-none-any.whl ( ...
- python可能会用到的网络基础
网络编程 1.两种构架:(1)C/S构架:client, server (2) B/S构架:browser,server 2.地址相关:(1)MAC地址,物理地址,唯一,但可以更改 (2)ip地址,网 ...
- (PMP)第2章-----项目运行环境
1.事业环境因素:(客观存在,可能有帮助或阻碍,项目经理必须遵守) 内部:文化,结构,治理:设施和资源的地理分布,基础设施,信息技术软件,资源可用性,员工能力 外部:市场条件,社会和文化,法律限制,商 ...
- python 实现rsa 的加密解密存读取(PEM格式证书)【转发】
来源:CSDN 原文:https://blog.csdn.net/sjt1996/article/details/83377800
- REdis MASTER aborted replication NOAUTH Authentication required
对于REdis集群,如果设置了requirepass,则一定要设置masterauth,否则从节点无法正常工作,查看从节点日志可以看到哪下内容:19213:S 22 Apr 2019 10:52:17 ...
- oracle远程连接服务器数据库
oracle远程连接数据库,需要配置本地服务,具体步骤如下: 1. 2.添加新的服务 3.输入服务名(例如:orcl3即服务器数据库名) 4.选择TCP协议 5.输入服务器IP(192.268.10. ...
- beautifulsoup爬取糗事百科
# _*_ coding:utf-8 _*_ import urllib2 from bs4 import BeautifulSoup user_agent = "Mozilla/5.0 ( ...
- hive on spark 参数设置
; ; set spark.executor.memory=5G;
- win7 docker Toolbox 启动Docker Quickstart Terminal 失败!
解决办法: 在windows下安装docker Toolbox 启动Docker Quickstart Terminal 失败! 主要是用如下文件启动,临时解决,或设置环境变量
- 包建强的培训课程(13):iOS与ReactNative
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...