华师一附中OI组

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1362|回复: 1
打印 上一主题 下一主题

替罪羊树

[复制链接]

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
跳转到指定楼层
楼主
发表于 2020-3-22 20:10:11 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
基本思路,不平衡的就把他打扁,然后重建。什么叫做不平衡呢?我们定义左儿子的节点数和右儿子的节点数差距较大就为不平衡,完美的平衡是左右儿子数量相等,左右儿子各占根总数的50%。但这个很难,一般定义左右儿子中某个数量超过根节点的alpha=75%为不平衡,需要重构。若alpha太大,>80%,重构次数太多,费时,太小<70%,树就不太平衡,影响以后查找和删除的效果。
如何重构呢?暴力点,先把这个子树拍平,中序遍历到一个数组,然后从中间开始二分类似线段树完美建树。
所以在一般的BST基础上,除了ls rs val cnt等之外还需要加上sz记录我这棵树下所有节点的数目。
  1. struct node
  2. {
  3.         int val,cnt;
  4.         int ls,rs,sz;  ///lsrs左右儿子  sz本子树下所有节点个数
  5. } a[N];
复制代码
重建过程
  1. void RB(int &rt)   ///重建rt
  2. {
  3.         if(rt==0) return;
  4.         float t=a[rt].sz*p; ///左右儿子不能大于rt的0.75
  5.         bool b1=a[a[rt].ls].sz>t;
  6.         bool b2=a[a[rt].rs].sz>t;

  7.         if( b1|| b2)
  8.                 {
  9.                         ita=0;
  10.                         Inorder(rt);
  11.                         rt=build(1,ita);
  12.                 }
  13. }
复制代码
拍扁的过程,其实就是递归中序遍历,不过是输出到临时数组
  1. void Inorder(int rt)   //倒到数组
  2. {
  3.         if(rt>0)
  4.                 {
  5.                         Inorder(a[rt].ls);
  6.                         ta[++ita]=rt;
  7.                         Inorder(a[rt].rs);
  8.                 }
  9. }
复制代码


重新完美建树的过程,完全类似线段树
  1. int build(int l, int r)   //l-r重新建树
  2. {
  3.         if(l>r) return 0;
  4.         int m=(l+r)/2;
  5.         a[ta[m]].ls=build(l,m-1);
  6.         a[ta[m]].rs=build(m+1,r);
  7.         pushup(ta[m]);
  8.         return ta[m];
  9. }
复制代码


回复

使用道具 举报

738

主题

1485

帖子

5422

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5422
沙发
 楼主| 发表于 2020-3-22 20:10:26 | 只看该作者
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1001005;
  4. const float p=0.75;
  5. struct node
  6. {
  7.         int val,cnt;
  8.         int ls,rs,sz;  ///lsrs左右儿子  sz本子树下所有节点个数
  9. } a[N];
  10. int ta[N],ita;///转移数组

  11. int n, tot, rt;
  12. void ArrayPr()
  13. {
  14.         for (int i=1; i<=tot; i++) cout<<setw(3)<<i;
  15.         cout<<endl;
  16.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].val;
  17.         cout<<endl;
  18.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].cnt;
  19.         cout<<endl;
  20.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].ls;
  21.         cout<<endl;
  22.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].rs;
  23.         cout<<endl;
  24.         for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].sz;
  25.         cout<<endl;

  26. }

  27. void BSTPr(int rt) ///中序输出以rt为根的BST
  28. {
  29.         if (a[rt].ls>0)  BSTPr(a[rt].ls);
  30.         cout<<a[rt].val<<' '; /// 也可以加上cnt
  31.         if (a[rt].rs>0)  BSTPr(a[rt].rs);
  32. }

  33. void pushup(int rt)
  34. {
  35.         int l=a[rt].ls, r=a[rt].rs;
  36.         a[rt].sz=a[l].sz+a[r].sz+a[rt].cnt;
  37. }

  38. int build(int l, int r)//l-r重新建树
  39. {
  40.         if(l>r) return 0;
  41.         int m=(l+r)/2;
  42.         a[ta[m]].ls=build(l,m-1);
  43.         a[ta[m]].rs=build(m+1,r);
  44.         pushup(ta[m]);
  45.         return ta[m];
  46. }

  47. void Inorder(int rt)//倒到数组
  48. {
  49.         if(rt>0)
  50.         {
  51.                 Inorder(a[rt].ls);
  52.                 ta[++ita]=rt;
  53.                 Inorder(a[rt].rs);
  54.         }
  55. }

  56. void RB(int &rt)  ///重建rt
  57. {
  58.         if(rt==0) return;
  59.         float t=a[rt].sz*p; ///左右儿子不能大于rt的0,75
  60.         bool b1=a[a[rt].ls].sz>t;
  61.         bool b2=a[a[rt].rs].sz>t;

  62.         if( b1|| b2)
  63.         {
  64.                 ita=0;
  65.                 Inorder(rt);
  66.                 rt=build(1,ita);
  67.         }
  68. }

  69. void Ins(int &rt, int x)
  70. {
  71.         if(rt==0)
  72.         {
  73.                 rt=++tot;
  74.                 a[rt].val=x;
  75.                 a[rt].sz=a[rt].cnt = 1;
  76.         }
  77.         else
  78.         {
  79.                 RB(rt); /// 比原版多一句调整
  80.                 if(a[rt].val==x) a[rt].cnt ++,a[rt].sz++;
  81.                 else
  82.                 {
  83.                         if(a[rt].val<x) Ins(a[rt].rs, x);
  84.                         else if(a[rt].val>x) Ins(a[rt].ls, x);
  85.                         pushup(rt); /// 比原版多一句调整  线段树相似
  86.                 }
  87.         }
  88. }

  89. int DelMin(int &rt)  ///原版不变
  90. {
  91.         int t;
  92.         if(a[rt].ls==0)///没有左儿子 那就是它
  93.         {
  94.                 t=rt;
  95.                 rt=a[rt].rs;
  96.                 return t;
  97.         }
  98.         else
  99.         {
  100.                 t=DelMin(a[rt].ls); ///递归
  101.                 pushup(rt);///多了这个调整
  102.                 return t;
  103.         }

  104. }

  105. void Del(int &rt,int x)
  106. {
  107.         if (rt==0) return ;
  108.         else if(a[rt].val>x)
  109.         {
  110.                 Del(a[rt].ls,x);
  111.                 pushup(rt);
  112.         }
  113.         else if(a[rt].val<x)
  114.         {
  115.                 Del(a[rt].rs,x);
  116.                 pushup(rt);
  117.         }
  118.         else if(a[rt].val==x)
  119.         {
  120.                 if(a[rt].cnt>1)a[rt].cnt--,a[rt].sz--;
  121.                 else if(a[rt].ls==0)rt=a[rt].rs;
  122.                 else if(a[rt].rs==0)rt=a[rt].ls;
  123.                 else
  124.                 {
  125.                         int t=DelMin(a[rt].rs);
  126.                         a[rt].val=a[t].val;
  127.                         a[rt].cnt=a[t].cnt;
  128.                         pushup(rt);
  129.                 }
  130.         }
  131. }

  132. int Rank(int rt, int x) ///在RT子树中比x小的数 +1
  133. {
  134.         if(a[rt].val==x) return a[a[rt].ls].sz+1; /// 找到了
  135.         if(a[rt].val<x) return a[a[rt].ls].sz +a[rt].cnt+ Rank(a[rt].rs,x);///左子树下面的都算
  136.         if(a[rt].val>x) return Rank(a[rt].ls,x);
  137. }

  138. int Getkth(int rt, int x) ///在RT子树中找第k小
  139. {
  140.         int l=a[a[rt].ls].sz;  /// RT的左儿子下的个数
  141.         if(l+1<=x && x<=l+a[rt].cnt) return a[rt].val;
  142.         if(l+1>x) return Getkth(a[rt].ls, x); ///左儿子里去找
  143.         if(l+a[rt].cnt<x) return Getkth(a[rt].rs, x-(l+a[rt].cnt));
  144. }

  145. int Pre(int rt, int x)
  146. {
  147.         int p = rt, ans;
  148.         while(p>0)
  149.         {
  150.                 if(x<=a[p].val) p=a[p].ls;
  151.                 else
  152.                 {
  153.                         ans=p;
  154.                         p=a[p].rs;
  155.                 }
  156.         }
  157.         return ans;
  158. }
  159. int Suc(int rt, int x)
  160. {
  161.         int p = rt, ans;
  162.         while(p>0)
  163.         {
  164.                 if(a[p].val<=x) p=a[p].rs;
  165.                 else
  166.                 {
  167.                         ans = p;
  168.                         p=a[p].ls;
  169.                 }
  170.         }
  171.         return ans;
  172. }

  173. int main()
  174. {
  175.         cin>>n;
  176.         while(n--)
  177.         {
  178.                 int opt, x;
  179.                 cin>>opt>>x;
  180.                 if(opt == 1) Ins(rt, x);
  181.                 if(opt == 2) Del(rt, x);
  182.                 if(opt == 3) cout<<Rank(rt, x)<<endl;
  183.                 if(opt == 4) cout<<Getkth(rt, x)<<endl;
  184.                 if(opt == 5) cout<<a[Pre(rt, x)].val<<endl;
  185.                 if(opt == 6) cout<<a[Suc(rt, x)].val<<endl;
  186.                 ///ArrayPr();
  187.                 ///BSTPr(rt);
  188.         }
  189.         return 0;
  190. }
复制代码
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|服务支持:DZ动力|华师一附中OI组  

GMT+8, 2024-12-27 02:45 , Processed in 0.109860 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表