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调到根节点的位置。

      /                             \
p x
/ \ Zig(x) / \
x <> -----------------> <> p
/ \ / \
<> <> <> <>
       /                             \
x p
/ \ Zag(x) / \
p <> <----------------- <> 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的更多相关文章

  1. 树-伸展树(Splay Tree)

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

  2. 纸上谈兵: 伸展树 (splay tree)[转]

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!  我们讨论过,树的搜索效率与树的深度有关.二叉搜索树的深度可能为n,这种情况下,每 ...

  3. K:伸展树(splay tree)

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

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

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

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

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

  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. c函数调用过程原理及函数栈帧分析

    转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707       今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比 ...

  2. HDU 5787 K-wolf Number (数位DP)

    K-wolf Number 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5787 Description Alice thinks an integ ...

  3. HDU 5441 Travel (并查集+数学+计数)

    题意:给你一个带权的无向图,然后q(q≤5000)次询问,问有多少对城市(城市对(u,v)与(v,u)算不同的城市对,而且u≠v)之间的边的长度不超过d(如果城市u到城市v途经城市w, 那么需要城市u ...

  4. c/c++字符串处理大集合

    rember this strncpy(a,b,5); a[5]='\0'; char a[10]; memset(a,'#',sizeof(a)); a[10]='\0'; 刚开始学C/C++时,一 ...

  5. WebView自适应并嵌套在ScrollView里

    大致思路:通过流的形式把网页抓取下来,然后对webView进行设置. 1.对webView进行设置 web.setWebViewClient(new WebViewClient() { @Overri ...

  6. 如何让自己的电脑发布ASP http://jingyan.baidu.com/article/19192ad853224ce53f570748.html

    怎样在WIN7系统下安装IIS | 浏览:122821 | 更新:2012-03-03 14:07 | 标签:windows7 1 2 3 4 5 6 7 分步阅读 在此根据多年的网站开发经验,把如何 ...

  7. Swift学习笔记一

    最近计划把Swift语言系统学习一下,然后将MagViewer用这种新语言重构一次,并且优化一下,这里记录一下Swift的学习笔记. Swift和Objective-C相比,在语法和书写形式上做了很多 ...

  8. QM课程02-外部功能

    质量计划 · 对质量计划和检验计划进行基本数据的管理 · 物料说明 · 检验计划 质量检验 · 触发检验 · 具有检验计划选择和样本计算的检验处理 · 打印采样和检验的车间文档 · 记录结果和缺陷 · ...

  9. Codeforces Round #179 (Div. 1) A. Greg and Array 离线区间修改

    A. Greg and Array Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/295/pro ...

  10. Codeforces Round #185 (Div. 2) B. Archer 水题

    B. Archer Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/312/problem/B D ...