Size Balanced Tree(SBT) 模板
首先是从二叉搜索树开始,一棵二叉搜索树的定义是:
1.这是一棵二叉树;
2.令x为二叉树中某个结点上表示的值,那么其左子树上所有结点的值都要不大于x,其右子树上所有结点的值都要不小于x。
由二叉搜索树的第二条定义,可以很方便地利用这种特点在二叉树中以O(logn)的渐进复杂度完成插入、查找、删除等操作。
但是这里还是有个问题,就是弄不好的话,一棵普通的二叉搜索树经过多次修改操作之后可能会导致整棵树左右“不平衡”,出现一边结点很多,另一边结点很少的情况,这样最终每一次的操作时间就会大大偏离O(logn),甚至最后退化成一条链。
平衡树就是通过一系列的操作,不断动态维持二叉搜索树的左右平衡,始终保证二叉搜索树的效率。
SBT全称Size Balanced Tree,是一种通过记录以每个结点为根的子树中的结点的个数size,然后进行二叉搜索树结构调整的方法,由OI选手陈启峰发明。
SBT除了一般的二叉搜索树性质之外,还有两条性质:
对于SBT中的每一个结点t,有:
1.s[right[t]]>=s[left[left[t]]],s[right[left[t]]];
2.s[left[t]]>=s[left[right[t]]],s[right[right[t]]].
即在上图中:
s[L]>=s[C],s[D];s[R]>=s[A],s[B].
这一性质能够保证整棵SBT能够在整体的结构上保持相对平衡,而为了保持这一额外的性质,需要通过不断的调整来完成。
调整即通过结点的左右旋完成,左右旋不改变二叉搜索树的性质,而能够调整SBT的size,使得整棵树能够始终保持为一棵SBT树。
- 保持-maintain
这是SBT的核心操作,保持过程通过左右旋来完成。
- 插入-insert
与普通的二叉搜索树相同,只是在插入完之后加入一个maintain操作。
- 删除-del
与普通的二叉搜索树相同。删除完一个结点之后虽然不能够保证此时的树还能够严格保持SBT性质,但是树结构从整体上来说最大深度不会增加,因此maintain操作就不需要进行了。
- 查找值-search
与普通的二叉搜索树相同。
- 查找第k个-select
由于size的存在,SBT天生就很适合处理查找第k个数这样的任务。不断确定一下左子树和右子树的size即可,若某结点左子树的size+1=k,那么该结点就是第k个数。
- 查找前趋/后继-pred/succ
与普通的二叉搜索树相同。
经过实际情况的分析,SBT中由于左右旋操作的存在,并不能严格保证与当前节点值相等的是在左边还是右边!!!因而应该不能够按照普通二叉搜索树的写法写......具体应该怎么写我还没有想到比较合适的方法(唉...囧...找了不少资料也基本上对这个都没怎么讲清楚)。下面贴的模板代码的pred和succ应该都是不合适的。
- 获取最大/最小值-getmax/getmin
与普通的二叉搜索树相同。向左或者向右走到底即可。
模板如下:
1 /* *********************************************** 2 MYID : Chen Fan 3 LANG : G++ 4 PROG : Size Balanced Tree 5 ************************************************ */ 6 7 #include <iostream> 8 #include <cstdio> 9 #include <cstring> 10 #include <algorithm> 11 12 using namespace std; 13 14 #define MAXN 1000 15 16 typedef struct sbtnod 17 { 18 int key,left,right,size; 19 } sbtnode; 20 int sbttail,sbt; 21 22 sbtnode tree[MAXN]; 23 24 void rrotate(int& t) 25 { 26 int k=tree[t].left; 27 if (!k) return ; 28 tree[t].left=tree[k].right; 29 tree[k].right=t; 30 tree[k].size=tree[t].size; 31 tree[t].size=tree[tree[t].left].size+tree[tree[t].right].size+1; 32 t=k; 33 } 34 35 void lrotate(int& t) 36 { 37 int k=tree[t].right; 38 if (!k) return ; 39 tree[t].right=tree[k].left; 40 tree[k].left=t; 41 tree[k].size=tree[t].size; 42 tree[t].size=tree[tree[t].left].size+tree[tree[t].right].size+1; 43 t=k; 44 } 45 46 void maintain(int& t,bool flag) 47 { 48 if (!t) return ; 49 if (!flag) 50 if (tree[tree[tree[t].left].left].size>tree[tree[t].right].size) rrotate(t); 51 else if (tree[tree[tree[t].left].right].size>tree[tree[t].right].size) 52 { 53 lrotate(tree[t].left); 54 rrotate(t); 55 } else return ; 56 else 57 if (tree[tree[tree[t].right].right].size>tree[tree[t].left].size) lrotate(t); 58 else if (tree[tree[tree[t].right].left].size>tree[tree[t].left].size) 59 { 60 rrotate(tree[t].right); 61 lrotate(t); 62 } else return ; 63 64 maintain(tree[t].left,false); 65 maintain(tree[t].right,true); 66 maintain(t,false); 67 maintain(t,true); 68 } 69 70 void insert(int& t,int v) 71 { 72 if (!t) 73 { 74 sbttail++; 75 tree[sbttail].key=v; 76 tree[sbttail].size=1; 77 t=sbttail; 78 } else 79 { 80 tree[t].size++; 81 if (v<tree[t].key) insert(tree[t].left,v); 82 else insert(tree[t].right,v); 83 maintain(t,v>=tree[t].key); 84 } 85 } 86 87 int del(int& t,int v) 88 { 89 int ret; 90 tree[t].size--; 91 if (v==tree[t].key||(v<tree[t].key&&tree[t].left==0)||(v>tree[t].key&&tree[t].right==0)) 92 { 93 ret=tree[t].key; 94 if (tree[t].left==0||tree[t].right==0) t=tree[t].left+tree[t].right;// 95 else tree[t].key=del(tree[t].left,tree[t].key+1); 96 } else 97 { 98 if (v<tree[t].key) ret=del(tree[t].left,v); 99 else ret=del(tree[t].right,v); 100 } 101 return ret; 102 } 103 104 int select(int t,int k) 105 { 106 if (k==tree[tree[t].left].size+1) return t; 107 if (k<=tree[tree[t].left].size) return select(tree[t].left,k); 108 else return select(tree[t].right,k-1-tree[tree[t].left].size); 109 } 110 111 int search(int t,int x) 112 { 113 if (t==0||x==tree[t].key) return t; 114 if (x<tree[t].key) return search(tree[t].left,x); 115 else return search(tree[t].right,x); 116 } 117 118 int getmin(int t) 119 { 120 int i=t; 121 while(tree[i].left) 122 { 123 i=tree[i].left; 124 } 125 return i; 126 } 127 128 int getmax(int t) 129 { 130 int i=t; 131 while(tree[i].right) 132 { 133 i=tree[i].right; 134 } 135 return i; 136 } 137 138 int pred(int& t,int y,int v) 139 { 140 if (!t) return y; 141 if (tree[t].key<v) return pred(tree[t].right,t,v); 142 else return pred(tree[t].left,y,v); 143 } 144 145 int succ(int& t,int y,int v) 146 { 147 if (!t) return y; 148 if (tree[t].key>=v) return succ(tree[t].left,t,v); 149 else return succ(tree[t].right,y,v); 150 } 151 152 void deb_out() 153 { 154 printf("-------\n"); 155 printf("sbttail=%d sbt=%d\n",sbttail,sbt); 156 for (int i=1;i<=sbttail;i++) 157 printf("%2d: key=%2d size=%2d left=%2d right=%2d\n",i,tree[i].key,tree[i].size,tree[i].left,tree[i].right); 158 printf("-------\n"); 159 } 160 161 int main() 162 { 163 memset(tree,0,sizeof(tree)); 164 sbttail=0; 165 sbt=0; 166 167 for (int i=1;i<=15;i++) insert(sbt,i); 168 deb_out(); 169 170 //printf("%d\n",del(sbt,8)); 171 insert(sbt,8); 172 deb_out(); 173 del(sbt,8); 174 del(sbt,8); 175 printf("%d\n",search(sbt,8)); 176 deb_out(); 177 }