SPlay 伸展树
来源:互联网 发布:mac强制关掉进程 编辑:程序博客网 时间:2024/06/06 09:48
Splay伸展树
伸展树(Splay Tree)是一种二叉排序树,能在最坏平摊LogN时间内完成插入、查找和删除操作。是一种自调整形式的二叉树,它会沿着某个节点到跟的路径,通过一系列旋转把这个节点搬到根节点去。
Splay树的基本操作:
1、旋转和伸展
2、查找
3、插入、删除
4、最大最小值
5、前驱后继
6、合并、分离
一般Splay的节点数据:
struct node{ int val,size; //节点值,节点大小 node * ch[2],*fa; //左右节点,父节点}*root=NULL;
Splay旋转:
void Rotate(Node *&x,int c){//c==0 左旋 c==1右旋 Node *y=x->fa; //PushDown(y); //PushDown(x); //PushDown(x->ch[c]); y->ch[!c] = x->ch[c]; if(x->ch[c] != NULL) x->ch[c]->fa = y; x->fa = y->fa; if(y->fa != NULL) if(y->fa->ch[0] == y) y->fa->ch[0] = x; else y->fa->ch[1] = x; x->ch[c] = y; y->fa = x; //UpDate(y); if(y == root) root=x;}
注意:前方高能
伸展操作:
情况一:节点x的父节点y是根节点。这时,如果x是p的左孩子,我们进行一次Zig(右旋)操作;如果x 是p 的右孩子,则我们进行一次Zag(左旋)操作。经过旋转,x成为二叉查找树的根节点,调整结束。即:如果当前结点父结点即为根结点,那么我们只需要进行一次简单旋转即可完成任务,我们称这种旋转为单旋转。如图1所示:
情况二:(一般称作“一字型旋转”)节点x 的父节点p 不是根节点,p 的父节点为g,且x 与p 同时是各自父节点的左孩子或者同时是各自父节点的右孩子。这时,我们进行一次Zig-Zig操作或者Zag-Zag操作。即:设当前结点为x , x 的父结点为p ,p 的父结点为g ,如果p 和x 同为其父亲的左孩子或右孩子,那么我们先旋转p ,再旋转x 。如图2所示:
情况三:(一般称作“之字型旋转”)节点x的父节点p不是根节点,p的父节点为g,x与p中一个是其父节点的左孩子而另一个是其父节点的右孩子。这时,我们进行一次Zig-Zag操作或者Zag-Zig 操作。即:这时我们连续旋转两次X 。如图3所示:
说明:这种情况先将x转到它的父亲的位置上,再与g旋转
SPLAY伸展操作代码:
// node 为结点类型,其中ch[0]表示左结点指针,ch[1]表示右结点指针 // pre 表示指向父亲的指针 void Rotate(Node *&x,int c){//c==0 左旋 c==1右旋 Node *y=x->fa; //PushDown(y); //PushDown(x); //PushDown(x->ch[c]); y->ch[!c] = x->ch[c]; if(x->ch[c] != NULL) x->ch[c]->fa = y; x->fa = y->fa; if(y->fa != NULL) if(y->fa->ch[0] == y) y->fa->ch[0] = x; else y->fa->ch[1] = x; x->ch[c] = y; y->fa = x; //UpDate(y); if(y == root) root=x;} void Splay(Node *&cur,Node *&f){//将cur转到f的位置 for(PushDown(cur); cur != f ;){ if(cur->fa == f) if(f->ch[0] == cur) Rotate(cur,1); else Rotate(cur,0); else { Node *y = cur->fa,*z = y->fa; if(z->ch[0] == y) if(y->ch[0] == cur) Rotate(y,1),Rotate(cur,1);//yi else Rotate(cur,0),Rotate(cur,1);//zhi else if(y->ch[1] == cur) Rotate(y,0),Rotate(cur,0);//yi else Rotate(cur,1),Rotate(cur,0);//zhi if(z==f) break; } //UpDate(cur); } //UpDate(cur);}
如果说要把某个节点转到根的位置,你可以假设还有一个须根,当前树的根永远吊在须根上!!(个人心得、仅供参考)
Splay树的递归建立
当建树节点的值是有序的时候,可以利用递归方式建树,类似于线段树的建树方式,效率还是比较高的。
void build(node *fa,node *&cur,int l,int r){ if(l>r)return; int mid=(l+r)>>1; cur=newnode(sz[mid]); cur->pre=fa; build(cur,cur->ch[0],l,mid-1); build(cur,cur->ch[1],mid+1,r); update(cur);}
Splay的节点查找
首先按照二叉排序树的性质(左子树都比它小,右子树都比它大),然后将该节点伸展到根节点。。
//查找键值为X的节点的位置并将其旋转到根节点 node *bst_search(node *cur,int x){ if(!cur) return NULL; if(cur->sh == x) return cur; else if(x > cur->sh) return bst_search(cur->ch[1],x); else if(x < cur->sh) return bst_search(cur->ch[0],x);}//bst_search 返回的是键值为x的元素的位置 node *search(node *cur,int x){ node *p=bst_search(cur,x); //此时p即为元素x的位置 splay(p,cur->pre); return p;}
找到处在中序遍历第k 个结点,并将其旋转到结点f 的下面:
void Select(int k, node *&f){ int tmp; node *t; for(t=root;;) { // 从根结点开始 Push_Down(t); // 由于要访问t 的子结点,将标记下传 tmp = t->ch[0]->size;//得到t 左子树的大小 if (k == tmp+1) break;//得出t 即为查找结点,退出循环 if (k <= tmp) // 第k 个结点在t 左边,向左走 t = t->ch[0]; else // 否则在右边,而且在右子树中,这个结点不再是第k 个 k -= tmp + 1, t = t->ch[1]; } Push_Down(t); splay(t,f); // 执行旋转}
Splay节点插入:
按照二叉排序树插入,然后将其旋转到根节点下
结构体里写上这个函数:
node *newnode(int x){ node *cur=new node; cur->val = x; cur->ch[0] = cur->ch[1] = cur->pre = NULL;}
然后是插入函数:
//插入值为x的一个新节点 void insert(node * cur,int x){ if(root==NULL){//cur不可改变,root要特判 root=newnode(x); return ; } node *p; while(cur!=NULL){ p=cur; if(cur->val>x) cur = cur->ch[0]; else cur = cur->ch[1]; } //在 while 过程当中 p始终保持是cur的父节点 cur=newnode(x); if(cur->val < p->val) p->ch[0]=cur; //注意判读条件,不可用cur==p->ch[0] else p->ch[1]=cur; cur->pre = p; splay(cur,NULL);}
Splay的合并:
将俩棵伸展树合并必须保证第二课伸展树的值都大于第一棵伸展树,只需要将第一棵伸展树的最大节点转到根的位置上,这时候该节点的有孩子为空(因为:该节点是最大的节点,如果他还有右孩子的话根据二叉排序树的定义他的右孩子比他大),直接将第二棵伸展树挂到第一棵伸展树的最大节点的右孩子上。。
node *merge(node *x,node *y){ if(!x) return y; if(!y) return x; while(x->ch[1]) x = x->ch[1]; splay(x,NULL); x->ch[1] = y; y->pre = x; return x;}
Splay分离:
方法:查找给定值得节点并伸展到根,返回它的左右孩子
//Splay 分离void split(node *cur,int x,node *&x,node *&y){ node *t=search(cur,x); x=cur->ch[0];x->pre=NULL; y=cur->ch[1];y->pre=NULL;}
Splay节点删除:
//值为x的节点删除 node *del(node *cur,int x){ node *t=search(cur,x); return merge(cur->ch[0],cur->ch[1]);}
上面的内容看看还行,代码就算了,代码是我粘的我们吕老师的,指针写的是又臭又长又丑,代码还的看我的。。
- 伸展树(splay树)
- Splay树(伸展树)
- Splay Tree(伸展树)
- Splay Tree 伸展树
- Splay伸展树&模板
- Splay 伸展树
- 伸展树splay tree
- Splay Tree(伸展树)
- splay tree(伸展树)
- 【数据结构】伸展树 Splay
- 伸展树(Splay tree)
- 伸展树(Splay tree)
- 伸展树-splay
- 伸展树splay+uva11922
- Splay Tree(伸展树)
- splay - tree 伸展树
- 伸展树Splay Tree
- bzoj1208 splay伸展树
- Java高效读取大文件
- 时间监视器 StopWatch
- 关于AES加解密
- 在linux中安装JDK和tomcat(一):在虚拟机上安装linux
- 文件IO编程十
- SPlay 伸展树
- 部分函数总结
- org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe
- C++抽象编程——算法分析(1)——选择排序
- 关于Java异常10件事儿<下>20170511
- Git冲突:commit your changes or stash them before you can merge.
- Java 9 中的 9 个新特性
- Java的堆和栈的内存分布
- 安装wamp集合环境过程中提示丢失msvcr110.dll的解决方法