之前在查treap树的时候,偶然在知乎看到一篇比treap树还简单的替罪羊树的介绍,传送门:https://zhuanlan.zhihu.com/p/21263304,大神还是写的很好的,有兴趣的可以去看下,当然也可以看我的总结。
一 、平衡条件
左子树大小 < alpha * 根大小 并且 右子树大小 < alpha * 根大小
二、 替罪羊树总结起来就两个操作:
1.拉平,当树不满足平衡条件时,把树伸展成链表。
2.重构,递归的选取链表的中点作为根节点重建平衡树,这样得到的几乎是完美的平衡二叉树。
三、基本操作
0.结构
struct scapegoat{
scapegoat* left;
scapegoat* right;
int val,size;
bool real;
scapegoat(int v,int s,bool r):val(v),size(s),real(r){left = right = NULL;}
};
1.插入
先正常的插入到二叉搜索树中,再回溯的过程中,判断是否违背了平衡条件,如果违背了,拉平并重构该树。
tree insert(tree t,int val){
if(t==NULL){
t = new scapegoat(val,1,1);
return t;
}
bool r = 0;(t->size)++;
if(val > t->val){
t->right = insert(t->right,val);
if(t->right->size > alpha*t->size+10){r=1;}
}
else if(val < t->val){
t->left = insert(t->left,val);
if(t->left->size > alpha*t->size+10){r=1;}
}
if(r){
qu.clear();tree2list(t);
t = rebuild(0,qu.size()-1);
}
return t;
}
2.删除
采用lazy删除,删除的给打上一个标记,每次重构都把lazy点删掉。
bool remove(tree t,int val){
if(t==NULL)return 0;
bool c;
if(val>t->val)c = remove(t->right,val);
else if(val<t->val)c = remove(t->left,val);
else{
if(t->real){t->real=0;c=1;}
else{c=0;}
}
if(c)(t->size)--;
return c;
}
3.总结
总体来说思想很简单,不过编程还是有一定复杂度的,更treap树差不太多,性能的话据说是平均O(log(n))的,不过我的代码貌似重构的很频繁,不知道是不是写错了。
现在研究的不深,之后有机会再补充。
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
struct scapegoat{
scapegoat* left;
scapegoat* right;
int val,size;//值域与存储真实点个数的size域
bool real;//该点是否是真实的
scapegoat(int v,int s,bool r):val(v),size(s),real(r){left = right = NULL;}
};
typedef scapegoat* tree;
const int alpha = 0.55;//权重因子
vector<tree> qu;
void tree2list(tree t){//拉平树
if(t==NULL)return;
if(t->left)tree2list(t->left);
if(t->real)qu.push_back(t);
if(t->right)tree2list(t->right);
if(!t->real)delete t;
}
tree rebuild(int left,int right){//重构树
if(left > right)return NULL;
int m = (left+right)/2;
tree t = qu[m];
t->size = right-left+1;
t->left = rebuild(left,m-1);
t->right = rebuild(m+1,right);
return t;
}
tree insert(tree t,int val){
if(t==NULL){
t = new scapegoat(val,1,1);
return t;
}
bool r = 0;(t->size)++;
if(val > t->val){
t->right = insert(t->right,val);
if(t->right->size > alpha*t->size+10){r=1;}
}
else if(val < t->val){
t->left = insert(t->left,val);
if(t->left->size > alpha*t->size+10){r=1;}
}
if(r){
cout << "dd" <<endl;
qu.clear();tree2list(t);
t = rebuild(0,qu.size()-1);
}
return t;
}
bool remove(tree t,int val){
if(t==NULL)return 0;
bool c;
if(val>t->val)c = remove(t->right,val);
else if(val<t->val)c = remove(t->left,val);
else{
if(t->real){t->real=0;c=1;}
else{c=0;}
}
if(c)(t->size)--;
return c;
}
void level(tree t){
if(!t)return;
tree now,last=t;
queue<tree> qu1;
qu1.push(t);
while(qu1.size()){
now = qu1.front();qu1.pop();
if(now->left)qu1.push(now->left);
if(now->right)qu1.push(now->right);
cout << now->val << "(" << now->size << ")" << " ";
if(now == last && qu1.size()){last = qu1.back();cout << endl;}
}
cout << endl;
}
int main(){
int i;
tree t=NULL;
for(i=0;i<100;i++){
t = insert(t,i);
}
level(t);
}