伸展树的学习(四):在已知的序列中任何位置插入数据

    前面讲解了伸展树的原始姿态:二叉搜索树!这篇文章呢就讲解一下伸展树的两种操作:右旋(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

 
 
  1. #include <iostream> 
  2.  
  3. #define INF ~0u>>1 
  4. #define MN 200005 
  5. #define NIL SPLAY 
  6. using namespace std; 
  7. struct SplayTree{ 
  8.          struct Node{ 
  9.              int key,size; 
  10.              int minv,add; 
  11.              bool rev; 
  12.              Node *left,*right,*father; 
  13.              Node(){} 
  14.              Node(int _key):key(_key){//开始我以为代码里并没有对key进行赋值,后来才发现“:key(_key)”这句代码已经对其赋值了 
  15.                  minv=_key,size=1,add=0,rev=false
  16.             } 
  17.          }SPLAY[MN],*SP,*root,*head,*tail; 
  18. // 
  19. //  //对伸展树进行初始化,也就是把head和tail加入到树中, 
  20.     void init(){ 
  21.         SP=NIL; 
  22.         NIL->key=NIL->minv=INF; 
  23.         NIL->size=0; 
  24.         NIL->rev=false;NIL->add=0;//用于懒操作的 
  25.         NIL->left=NIL->right=NIL->father=NIL; 
  26.         head=new(++SP)Node(INF); 
  27.         head->left=head->right=head->father=NIL; 
  28.         tail=new(++SP)Node(INF); 
  29.         tail->left=tail->right=tail->father=NIL; 
  30.         head->right=tail;tail->father=head; 
  31.         head->size++; 
  32.         root=head; 
  33.     } 
  34.  
  35.     //更新结点的值 
  36.     void update(Node *&t){ 
  37.         t->size=t->left->size+t->right->size+1; 
  38.         t->minv=min(t->key,min(t->left->minv,t->right->minv)); 
  39.     } 
  40.     //结点t往右旋转 
  41.     void zig(Node *&t){//表示t为f的左孩子 
  42.         Node *f=t->father,*r=t->right;//因为t的左结点不会变,而右结点要接到t->father的左边 
  43.         t->father=f->father;//这句很好理解:因为t要旋转到t->father的位置 
  44.         if(f==root) root=t; 
  45.         else
  46.             if(f->father->left==f) f->father->left=t; 
  47.             else f->father->right=t; 
  48.         } 
  49.         t->right=f,r->father=f,f->father=t,f->left=r; 
  50.          update(f);update(t); 
  51.     } 
  52.     //结点t往左旋转 
  53.     void zag(Node *&t){ 
  54.         Node *f=t->father,*ll=t->left;//因为t的右结点不会变,而左结点要接到t->father的右边 
  55.         t->father=f->father;//这句很好理解:因为t要旋转到t->father的位置 
  56.         if(f==root) root=t; 
  57.         else
  58.             if(f->father->left==f) f->father->left=t; 
  59.             else f->father->right=t; 
  60.         } 
  61.         t->left=f,ll->father=f,f->father=t,f->right=ll; 
  62.          update(f);update(t); 
  63.     } 
  64.     //暂时只维护区间,不作伸展操作 
  65.     void splay(Node *&root,Node *&t){ 
  66.          while(root!=t){ 
  67.              if(t->father==root){ 
  68.                  if(t->father->left==t) zig(t); 
  69.                  else zag(t); 
  70.              } 
  71.              else
  72.                  if(t->father->father->left==t->father){ 
  73.                      if(t->father->left==t) zig(t->father),zig(t); 
  74.                      else zag(t),zig(t); 
  75.                  }else
  76.                      if(t->father->left==t) zig(t),zag(t); 
  77.                      else zag(t->father),zag(t); 
  78.                  } 
  79.              } 
  80.          } 
  81.     } 
  82.     //往第pos位后插入key 
  83.     void insert(int key,int pos){ 
  84.         Node *t;t=new(++SP)Node(key); 
  85.         t->left=t->right=t->father=NIL; 
  86.  
  87.         Node *r,*p;r=root; 
  88.         bool flag=false;//默认朝左边走 
  89.         while(r!=NIL){ 
  90.             p=r;r->size++; 
  91.             if(r->left->size+1>pos)r=r->left,flag=false
  92.             else pos-=r->left->size+1,r=r->right,flag=true
  93.         } 
  94.         if(flag)p->right=t; 
  95.         else p->left=t; 
  96.         t->father=p; 
  97.         splay(root,t); 
  98.     } 
  99.     void inorder(Node *t){ 
  100.         if(t->left!=NIL) 
  101.             inorder(t->left); 
  102.         printf("%d ",t->key); 
  103.         if(t->right!=NIL) 
  104.             inorder(t->right); 
  105.  
  106.     } 
  107. }tree; 
  108. //这里需要注意的是,SplayTree一定要定义成全局变量,不然会因为栈溢出而报内存错误 
  109. int main(){ 
  110.     int a[10]={0,1,2,3,4,5};//a[0]位置的数据不用 
  111.     int i; 
  112.     //初始化 
  113.     tree.init(); 
  114.     //初始化序列数据 
  115.     for(i=1;i<5;i++){ 
  116.         tree.insert(a[i],i); 
  117.     } 
  118.     //把0插入到数列的第4个位置 
  119.     tree.insert(0,4); 
  120.     //按下标的顺序输出 
  121.     tree.inorder(tree.root); 
  122.  
  123.     return 0; 

   程序输出的结果:2147483647 1 2 3 0 4 5 2147483647 除了head和tail,就正好是我们维护的序列了(head和tail的key值为INF,即0x7FFFFFFF=(2147483647))。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值