伸展树的学习(五):删除某个特定的结点

    前面讲解了插入结点的操作过程,本文就分析一下程序是如何删除一个结点呢?

 

伸展树的删除图解:

 

假如伸展树结构如下图,而我们要删除结点6(根结点),操作的过程是怎么样的呢?(这里的图有一点错误:tail的父亲应该是:10,而不是1)

第一步:首先把结点6旋转到根结点(select操作)这样删除结点6的操作就变成了删除根结点的操作了。(这里的图有一点错误:tail的父亲应该是:10,而不是1)

第二步:如果根结点没有左孩子,那么直接把根结点的右孩子作为新的根结点,完成删除操作;类似的,如果根结点没有右孩子,那么把根结点的左孩子作为新的根结点,完成删除操作。

如果根结点有两个孩子,先把根结点左子树中最大的节点旋转到左孩子的位置(这样做保证了左孩子的右孩子为空)然后呢把根结点的右孩子挂接到左孩子的右孩子位置。

本图中结点5是整棵子树中最大的节点,所以不用旋转了。

 

第三步:把根结点6的右孩子8作为左孩子5的右孩子!(有点难懂!!还是看图吧!)然后把结点5设定为根结点。(这里的图有一点错误:tail的父亲应该是:10,而不是1)

 

这样就完成了一个结点的删除操作!这样很细致的一分析就发现思路清晰了,挺容易理解了!

刚开始我还有一个疑问,如果就这样删除了那么各结点的size怎么调整呢?后来发现我多虑了!在结点splay的过程中已经调整好了,也就是说,删除是不影响其它结点的size大小的,最后加一句:update(root);就搞定一切了! 这整个过程一定要对左旋(zag)和右旋(zig)理解清楚!!

 

  最后把删除的代码贴出来吧!

 
 
  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,ishead; 
  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;ishead=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->ishead=true
  28.         head->left=head->right=head->father=NIL; 
  29.         tail=new(++SP)Node(INF); 
  30.         tail->left=tail->right=tail->father=NIL; 
  31.         head->right=tail;tail->father=head; 
  32.         head->size++; 
  33.         root=head; 
  34.     } 
  35.  
  36.     //更新结点的值 
  37.     void update(Node *&t){ 
  38.         t->size=t->left->size+t->right->size+1; 
  39.         t->minv=min(t->key,min(t->left->minv,t->right->minv)); 
  40.     } 
  41.     //结点t往右旋转 
  42.     void zig(Node *&t){//表示t为f的左孩子 
  43.         Node *f=t->father,*r=t->right;//因为t的左结点不会变,而右结点要接到t->father的左边 
  44.         t->father=f->father;//这句很好理解:因为t要旋转到t->father的位置 
  45.         if(f==root) root=t; 
  46.         else
  47.             if(f->father->left==f) f->father->left=t; 
  48.             else f->father->right=t; 
  49.         } 
  50.         t->right=f,r->father=f,f->father=t,f->left=r; 
  51.          update(f);update(t); 
  52.     } 
  53.     //结点t往左旋转 
  54.     void zag(Node *&t){ 
  55.         Node *f=t->father,*ll=t->left;//因为t的右结点不会变,而左结点要接到t->father的右边 
  56.         t->father=f->father;//这句很好理解:因为t要旋转到t->father的位置 
  57.         if(f==root) root=t; 
  58.         else
  59.             if(f->father->left==f) f->father->left=t; 
  60.             else f->father->right=t; 
  61.         } 
  62.         t->left=f,ll->father=f,f->father=t,f->right=ll; 
  63.          update(f);update(t); 
  64.     } 
  65.     //暂时只维护区间,不作伸展操作 
  66.     void splay(Node *&root,Node *&t){ 
  67.          while(root!=t){ 
  68.              if(t->father==root){ 
  69.                  if(t->father->left==t) zig(t); 
  70.                  else zag(t); 
  71.              } 
  72.              else
  73.                  if(t->father->father->left==t->father){ 
  74.                      if(t->father->left==t) zig(t->father),zig(t); 
  75.                      else zag(t),zig(t); 
  76.                  }else
  77.                      if(t->father->left==t) zig(t),zag(t); 
  78.                      else zag(t->father),zag(t); 
  79.                  } 
  80.              } 
  81.          } 
  82.     } 
  83.     //往第pos位后插入key 
  84.     void insert(int key,int pos){ 
  85.         Node *t;t=new(++SP)Node(key); 
  86.         t->left=t->right=t->father=NIL; 
  87.  
  88.         Node *r,*p;r=root; 
  89.         bool flag=false;//默认朝左边走 
  90.         while(r!=NIL){ 
  91.             p=r;r->size++; 
  92.             if(r->left->size+1>pos)r=r->left,flag=false
  93.             else pos-=r->left->size+1,r=r->right,flag=true
  94.         } 
  95.         if(flag)p->right=t; 
  96.         else p->left=t; 
  97.         t->father=p; 
  98.         splay(root,t); 
  99.     } 
  100.     //把pos位置的结点旋转到根结点 
  101.      void select(Node *&root,int pos){ 
  102.          Node *r=root; 
  103.          while(r->left->size+1!=pos){ 
  104.              if(r->left->size+1>pos) r=r->left; 
  105.              else pos-=r->left->size+1,r=r->right; 
  106.          } 
  107.          splay(root,r); 
  108.      } 
  109.      //删除结点 
  110.      void remove(int pos){ 
  111.          select(root,pos); 
  112.          if(root->left==NIL) root=root->right; 
  113.          else if(root->right==NIL) root=root->left; 
  114.          else
  115.              select(root->left,root->left->size); //这一句保证了root的左孩子的右孩子为空
  116.              root->left->right=root->right; 
  117.              root->right->father=root->left; 
  118.              root=root->left; 
  119.          } 
  120.          root->father=NIL; 
  121.          update(root); 
  122.      } 
  123.     void inorder(Node *t){ 
  124.         if(t->left!=NIL) 
  125.             inorder(t->left); 
  126.         printf("%d ",t->key); 
  127.         if(t->right!=NIL) 
  128.             inorder(t->right); 
  129.  
  130.     } 
  131. }tree; 
  132. //这里需要注意的是,SplayTree一定要定义成全局变量,不然会因为栈溢出而报内存错误 
  133. int main(){ 
  134.     int a[10]={0,1,2,3,4,5};//a[0]位置的数据不用 
  135.     int i; 
  136.     //初始化 
  137.     tree.init(); 
  138.     //初始化序列数据 
  139.     for(i=1;i<=3;i++){ 
  140.         tree.insert(a[i],i); 
  141.     } 
  142. //  tree.inorder(tree.root);cout<<endl; 
  143.     tree.remove(3);//删除结点2 因为我们根结点的左边多了一个head结点,所以加1
  144.     //按下标的顺序输出 
  145.     tree.inorder(tree.root); 
  146.  
  147.     return 0; 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值