前面讲解了伸展树的原始姿态:二叉搜索树!这篇文章呢就讲解一下伸展树的两种操作:右旋(zag)和左旋(zig)操作:
伸展树右旋的图示:(旋转的结果就是:使结点t的位置向上移动:人往高处走嘛)
右旋有一个前提:结点t是结点f的左孩子!根据二叉搜索树的性质:各结点满足以下不等式:a<t<=b<f<=r。旋转后,这个等式也不能变!这样里能理解a,b,r结点的挂接情况了
同样的左旋也也可类比过来,
伸展树左旋的图示:(旋转的结果就是:使结点T的位置向根结点移动,也就是使其深度更小:人往高处走嘛)
|
关于旋转,有的程序函数名为:zig() zag(),有的直接就称为:rotate(),其实都是一样的!没什么区别!
简单的伸展操作了解了,我们看一下POJ3580中的要求。
POJ3580里有这样一种操作要求:INSERT x P: insert P after Ax. For example, performing "INSERT 2 4" on {1, 2, 3, 4, 5} results in {1, 2, 4, 3, 4, 5},就是在序列的指定位置处插入一个数!
像这样的操作用线段树是很难实现的,但是用伸展树就很容易了!
比如序列:1 2 3 4 5,我们要在3的后面插入0,使序列变成:1 2 3 0 4 5
- #include <iostream>
- #define INF ~0u>>1
- #define MN 200005
- #define NIL SPLAY
- using namespace std;
- struct SplayTree{
- struct Node{
- int key,size;
- int minv,add;
- bool rev;
- Node *left,*right,*father;
- Node(){}
- Node(int _key):key(_key){//开始我以为代码里并没有对key进行赋值,后来才发现“:key(_key)”这句代码已经对其赋值了
- minv=_key,size=1,add=0,rev=false;
- }
- }SPLAY[MN],*SP,*root,*head,*tail;
- //
- // //对伸展树进行初始化,也就是把head和tail加入到树中,
- void init(){
- SP=NIL;
- NIL->key=NIL->minv=INF;
- NIL->size=0;
- NIL->rev=false;NIL->add=0;//用于懒操作的
- NIL->left=NIL->right=NIL->father=NIL;
- head=new(++SP)Node(INF);
- head->left=head->right=head->father=NIL;
- tail=new(++SP)Node(INF);
- tail->left=tail->right=tail->father=NIL;
- head->right=tail;tail->father=head;
- head->size++;
- root=head;
- }
- //更新结点的值
- void update(Node *&t){
- t->size=t->left->size+t->right->size+1;
- t->minv=min(t->key,min(t->left->minv,t->right->minv));
- }
- //结点t往右旋转
- void zig(Node *&t){//表示t为f的左孩子
- Node *f=t->father,*r=t->right;//因为t的左结点不会变,而右结点要接到t->father的左边
- t->father=f->father;//这句很好理解:因为t要旋转到t->father的位置
- if(f==root) root=t;
- else{
- if(f->father->left==f) f->father->left=t;
- else f->father->right=t;
- }
- t->right=f,r->father=f,f->father=t,f->left=r;
- update(f);update(t);
- }
- //结点t往左旋转
- void zag(Node *&t){
- Node *f=t->father,*ll=t->left;//因为t的右结点不会变,而左结点要接到t->father的右边
- t->father=f->father;//这句很好理解:因为t要旋转到t->father的位置
- if(f==root) root=t;
- else{
- if(f->father->left==f) f->father->left=t;
- else f->father->right=t;
- }
- t->left=f,ll->father=f,f->father=t,f->right=ll;
- update(f);update(t);
- }
- //暂时只维护区间,不作伸展操作
- void splay(Node *&root,Node *&t){
- while(root!=t){
- if(t->father==root){
- if(t->father->left==t) zig(t);
- else zag(t);
- }
- else{
- if(t->father->father->left==t->father){
- if(t->father->left==t) zig(t->father),zig(t);
- else zag(t),zig(t);
- }else{
- if(t->father->left==t) zig(t),zag(t);
- else zag(t->father),zag(t);
- }
- }
- }
- }
- //往第pos位后插入key
- void insert(int key,int pos){
- Node *t;t=new(++SP)Node(key);
- t->left=t->right=t->father=NIL;
- Node *r,*p;r=root;
- bool flag=false;//默认朝左边走
- while(r!=NIL){
- p=r;r->size++;
- if(r->left->size+1>pos)r=r->left,flag=false;
- else pos-=r->left->size+1,r=r->right,flag=true;
- }
- if(flag)p->right=t;
- else p->left=t;
- t->father=p;
- splay(root,t);
- }
- void inorder(Node *t){
- if(t->left!=NIL)
- inorder(t->left);
- printf("%d ",t->key);
- if(t->right!=NIL)
- inorder(t->right);
- }
- }tree;
- //这里需要注意的是,SplayTree一定要定义成全局变量,不然会因为栈溢出而报内存错误
- int main(){
- int a[10]={0,1,2,3,4,5};//a[0]位置的数据不用
- int i;
- //初始化
- tree.init();
- //初始化序列数据
- for(i=1;i<5;i++){
- tree.insert(a[i],i);
- }
- //把0插入到数列的第4个位置
- tree.insert(0,4);
- //按下标的顺序输出
- tree.inorder(tree.root);
- return 0;
- }
程序输出的结果:2147483647 1 2 3 0 4 5 2147483647 除了head和tail,就正好是我们维护的序列了(head和tail的key值为INF,即0x7FFFFFFF=(2147483647))。