伸展树 Splay Tree
Splay Tree 是二叉查找树的一种,它与平衡二叉树、红黑树不同的是,Splay Tree从不强制地保持自身的平衡,每当查找到某个节点n的时候,在返回节点n的同时,Splay Tree会将节点n旋转到树根的位置,这样就使得Splay Tree天生有着一种类似缓存的能力,因为每次被查找到的节点都会被搬到树根的位置,所以当80%的情况下我们需要查找的元素都是某个固定的节点,或者是 一部分特定的节点时,那么在很多时候,查找的效率会是O(1)的效率!当然如果查找的节点是很均匀地分布在不同的地方时,Splay Tree的性能就会变得很差了,但Splay Tree的期望的时间复杂度还是O(nlogn)的。
为了使访问的节点调到树根,必定要有像维护平衡二叉树那样的旋转操作,来维持二叉树节点间的偏序关系;与平衡二叉树类似,Splay有2种旋转操作(左旋zag、右旋zig)和4种旋转情况(LL,LR,RL,RR);旋转操作要保持二叉查找树的性质,通过对访问点x的位置来判断旋转情况,对应的情况使用对应的旋转操作序列,就可以让x的所属层上升,循环对x操作就可以把x调到根节点的位置。
/ \ |
/ \ |
struct node
{
int data;
node *left,*right,*father;
node(int d=,node* a=NULL,node *b=NULL,node *c=NULL):data(d),left(a),right(b),father(c){}
}*root;
void zig(node *k)
{
node* fa=k->father;
fa->left=k->right;
if (k->right) k->right->father=fa;
k->right=fa;
k->father=fa->father;
fa->father=k;
if (!k->father) return;
if (k->father->data>k->data)
k->father->left=k;
else
k->father->right=k;
}
void zag(node *k)
{
node* fa=k->father;
fa->right=k->left;
if (k->left) k->left->father=fa;
k->left=fa;
k->father=fa->father;
fa->father=k;
if (!k->father) return;
if (k->father->data>k->data)
k->father->left=k;
else
k->father->right=k;
}
void splay(node *k,node *&root)
{
while (k->father)
{
node *fa=k->father;
if (fa->father==NULL)
{
if (k==fa->left) zig(k);
else zag(k); } else
{
node *gf=fa->father;
if (fa==gf->left && k==fa->left){zig(fa);zig(k);}
if (fa==gf->left && k==fa->right){zag(k);zig(k);}
if (fa==gf->right && k==fa->left){zig(k);zag(k);}
if (fa==gf->right && k==fa->right){zag(fa);zag(k);}
}
}
root=k;
}
Splay旋转调整代码
插入操作是先在二叉树中找到该插入的位置并插入节点,到这里和普通二叉查找树的操作是一样的,之后要对新插入的节点执行Splay()旋转调整操作。
node* __insert(int data,node *&p)
{ if (p==NULL)
{
p=new node(data);
return p;
}
if (data<p->data)
{
node *q=__insert(data,p->left);
p->left->father=p;
return q;
} else
{
if (data==p->data) return NULL; //Splay Tree中不允许出现相同的数据,否则在旋转是会出错,导致死循环
node *q=__insert(data,p->right);
p->right->father=p;
return q;
}
}
void insert(int data,node *&root)
{
node *t=__insert(data,root);
if (t)splay(t,root);
}
插入操作
查找操作也是和二叉树中的步骤类似,只是最后要对找到点执行Splay().
node* __find(int data,node *root)
{
if (root==NULL) return NULL;
if (data==root->data) return root;
if (data<root->data) return __find(data,root->left);
return __find(data,root->right);
}
node* find(int data,node *&root)
{
node *q=__find(data,root);
if (q) splay(q,root);
return q;
}
查找操作
树的合并操作会比较麻烦些,先要用到求树的最大、最小操作,就是把树的最值提到树根,这样树根的左子树或是右子树就是一个空树,如果另一颗树最值操作后的树根满足条件,就可以直接连接上了。那如果不满足条件,就需要把另一树分为2棵树,其中一颗要满足条件,这样合并后还是两棵树,但有一棵树的大小会减少,这样循环下去最终就能和为一棵树了。
为了简单起见,下面我定义的合并函数只适用于同一根节点的两棵子树合并。
node *findmax(node *&p) //最大操作
{
node *t=p;
while (t->right) t=t->right;
splay(t,p);
return t;
}
node* join(node *a,node *b) //a是左孩子 b是右孩子
{
a->father=b->father=NULL;
if (!a || !b) return (node *)((int)a|(int)b);
node *t=findmax(a);
t->right=b;
b->father=t;
return t;
}
有条件的合并操作
要删除一个节点就把这个节点调到根节点,然后把左右孩子树合并,释放要删除节点就可以了。
void remove(int data,node *&root)
{
node *q=find(data,root);
if (q)
{
node *tem=root;
root=join(root->left,root->right);
delete tem;
}
}
删除操作
可以见得Splay Tree 内存的访问次数是蛮高的
伸展树 Splay Tree的更多相关文章
- 树-伸展树(Splay Tree)
伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二 ...
- 纸上谈兵: 伸展树 (splay tree)[转]
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 我们讨论过,树的搜索效率与树的深度有关.二叉搜索树的深度可能为n,这种情况下,每 ...
- K:伸展树(splay tree)
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...
- 高级搜索树-伸展树(Splay Tree)
目录 局部性 双层伸展 查找操作 插入操作 删除操作 性能分析 完整源码 与AVL树一样,伸展树(Splay Tree)也是平衡二叉搜索树的一致,伸展树无需时刻都严格保持整棵树的平衡,也不需要对基本的 ...
- 【BBST 之伸展树 (Splay Tree)】
最近“hiho一下”出了平衡树专题,这周的Splay一直出现RE,应该删除操作指针没处理好,还没找出原因. 不过其他操作运行正常,尝试用它写了一道之前用set做的平衡树的题http://codefor ...
- 伸展树(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也算是平衡 ...
随机推荐
- Umbraco Forms 中的Recaptcha遇到的问题
在Umbraco Form中添加Recaptcha时,不能把它设置成Mandatory, 否则就会出错
- 开源原生JavaScript插件-CJPCD(省市区联动)
一.前言 上两篇博客笔者对 JavaScript Module 模式,闭包等知识点做了简单介绍之后,我们今天开始正式开发一款属于自己的 JavaScript 插件.由于最近项目刚好用到地区选择这一块的 ...
- 《精通ASP.NET MVC5》第2章 第一个MVC应用程序
控制器 public class NewHomeController : Controller { // GET: /NewHome/ public ...
- INV(库存管理)
物料 PROCEDURE update_item(p_init_msg_list IN VARCHAR2 DEFAULT fnd_api.g_false, x_return_status OUT NO ...
- Hadoop on Mac with IntelliJ IDEA - 2 解决URI错误导致Permission denied
本文讲述在IntelliJ IDEA中使用FileSystem.copyFromLocalFile操作Hadoop时因URI格式有误导致Permission denied的解决过程. 环境:Mac O ...
- webpack echarts配置实例
简单介绍 本例介绍怎样在webpack中构建依赖echats的项目,echarts有好几种方式引入项目: 标签单文件引入:自1.3.5開始,ECharts提供标签式引入.假设项目本身并非基于模块化开发 ...
- Get和Post的参数传值
1. get是从服务器上获取数据,post是向服务器传送数据. 2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到.post是通过 ...
- [置顶] SNMP协议详解<二>
上一篇文章讲解了SNMP的基本架构,本篇文章将重点分析SNMP报文,并对不同版本(SNMPv1.v2c.v3)进行区别! 四.SNMP协议数据单元 在SNMP管理中,管理站(NMS)和代理(Agent ...
- QT5 串口收发实例代码
以下代码是自己测试门禁系统使用的 主要用到了串口的接收和发送 开发环境:xp QT5.1.1 串口:38400 N 8 1 自动检测可用串口 在xp上测试没问题 有些usb转串口会出现波特 ...
- Android短信彩信收发流程(应用层)
下图为ComposeMessageActivity中confirmSendMessageIfNeeded部分的信息发送流程.主要以接收者有效性的确认为主,然后转向sendMessage方法进行发送. ...