替罪羊树是计算机科学中,一种基于部分重建的自平衡二叉搜索树。
在替罪羊树上,插入或删除节点的平摊最坏时间复杂度是O(log n),搜索节点的最坏时间复杂度是O(log n)。
替罪羊树的主要思想就是将不平衡的树压成一个序列,然后暴力重构成一颗平衡的树。
在非平衡的二叉搜索树中,每次操作以后检查操作路径,找到最高的不平衡的结点,重建整个子树。
这里的平衡指的是:
对于某个
满足这个节点的两棵子树的 size 都不超过以该节点为根的子树的
乘以
,或被删除的节点数小于子树
的30%,那么就称这个子树是平衡的。
平衡因子
一般选择0.75 。
至于复杂度,虽说是重构,但复杂度并不高,均摊下来替罪羊树的总复杂度是O(logn)的。
暴力即优雅
存储
struct node
{
int lc,rc;
int val; //值
int cnt; //相同节点个数.若为零,则该节点不存在.
int size,fact; //全部节点个数和实际节点个数
#define lc(x) tzy[x].lc
#define rc(x) tzy[x].rc
#define c(x) tzy[x].cnt
#define s(x) tzy[x].size
#define f(x) tzy[x].fact
#define v(x) tzy[x].val
};
node tzy[N];
重构
0.检查是否平衡
void check(int &k,const int &end) //自上而下检查,这样可以防止一个部分被重构两次
{
if(k==end) return;
if(!balance(k)) {
rebuild(k);
update(root,k);
return;
}
if(v(end)<v(k)) check(lc(k),end);
else check(rc(k),end);
return;
}
1.核心
void rebuild(int &k)
{
tmp.clear();
ldr(k);
if(tmp.empty()) { k=0; return; }
lift(0,tmp.size()-1,k);
return;
}
2.判断是否平衡
const double alpha = 0.75;
bool balance(const int &k)
{
if(max(s(lc(k)),s(rc(k)))>s(k)*alpha||s(k)-f(k)>s(k)*0.3)
return false;
return true;
}
3.中序遍历(拍扁操作)
将得到一个上升的序列
vector<int> tmp;
void ldr(const int &k)
{
if(!k) return;
ldr(lc(k));
if(c(k))
tmp.push_back(k);
ldr(rc(k));
return;
}
4.分治重构
void lift(int l,int r,int &k)
{
if(l==r) {
k=tmp[l];
s(k)=f(k)=c(k);
lc(k)=rc(k)=0;
return;
}
int mid=(l+r)>>1;
k=tmp[mid];
if(l<mid) lift(l,mid-1,lc(k));
else lc(k)=0;
lift(mid+1,r,rc(k));
s(k)=s(lc(k))+s(rc(k))+c(k);
f(k)=f(lc(k))+f(rc(k))+c(k);
return;
}
5.更新size
void update(const int &k,const int &end)
{
if(k==end) return;
if(v(end)<v(k))
update(lc(k),end);
else update(rc(k),end);
s(k)=s(lc(k))+s(rc(k))+c(k);
return;
}
插入
void insert(int &k,const int &key)
{
if(!k) { //申请新节点
k=++pool;
v(k)=key;
lc(k)=rc(k)=0;
c(k)=s(k)=f(k)=1;
check(root,k); //检查
return;
}
++s(k); ++f(k);
if(v(k)==key) ++c(k);
else if(key<v(k))
insert(lc(k),key);
else insert(rc(k),key);
return;
}
删除
void erase(int &k,const int &key)
{
if(v(k)==key&&c(k)) {
--f(k); --c(k); //只打标记,不真正删除.
check(root,k); //检查
return;
}
--f(k);
if(key<v(k)) erase(lc(k),key);
else erase(rc(k),key);
return;
}
查询 x 数的排名
(若有多个相同的数,因输出最小的排名)
inline int getrank(const int &key)
{
int now=root,res=1;
while(now) {
if(key==v(now)) return res+f(lc(now));
if(key<v(now)) now=lc(now);
else res+=f(lc(now))+c(now),now=rc(now);
}
return res;
}
查询排名为 x 的数
inline int getnum(int rank)
{
int now=root;
while(now) {
if(c(now)>0&&f(lc(now))<rank&&f(lc(now))+c(now)>=rank)
return v(now);
if(f(lc(now))>=rank) now=lc(now);
else rank-=f(lc(now))+c(now),now=rc(now);
}
return 0;
}
这里需要一个查找操作
inline int find(const int &key)
{
int now=root;
while(now) {
if(v(now)==key) { return c(now) ? now : 0; }
if(key<v(now)) now=lc(now);
else now=rc(now);
}
return now;
}
//若存在返回 的下标,否则返回0;
求 x 的前趋
(前趋定义为小于 x,且最大的数)
inline int Query_pre(const int &key)
{
return getnum(getrank(key)-1);
}
求 x 的后继
(后继定义为大于 x,且最小的数)
inline int Query_suf(const int &key)
{
return getnum(getrank(key)+c(find(key)));
}
完整代码
//普通平衡树
#include<bits/stdc++.h>
#define INF 2147483646
using namespace std;
const int N = 1e6+5;
const double alpha = 0.75;
struct node
{
int lc,rc;
int val;
int cnt;
int size,fact;
#define lc(x) tzy[x].lc
#define rc(x) tzy[x].rc
#define c(x) tzy[x].cnt
#define s(x) tzy[x].size
#define f(x) tzy[x].fact
#define v(x) tzy[x].val
};
node tzy[N];
int pool,root;
vector<int> tmp;
bool balance(const int &k)
{
if(max(s(lc(k)),s(rc(k)))>s(k)*alpha||s(k)-f(k)>s(k)*0.3)
return false;
return true;
}
void ldr(const int &k)
{
if(!k) return;
ldr(lc(k));
if(c(k))
tmp.push_back(k);
ldr(rc(k));
return;
}
void lift(int l,int r,int &k)
{
if(l==r) {
k=tmp[l];
s(k)=f(k)=c(k);
lc(k)=rc(k)=0;
return;
}
int mid=(l+r)>>1;
k=tmp[mid];
if(l<mid) lift(l,mid-1,lc(k));
else lc(k)=0;
lift(mid+1,r,rc(k));
s(k)=s(lc(k))+s(rc(k))+c(k);
f(k)=f(lc(k))+f(rc(k))+c(k);
return;
}
void rebuild(int &k)
{
tmp.clear();
ldr(k);
if(tmp.empty()) { k=0; return; }
lift(0,tmp.size()-1,k);
return;
}
void update(const int &k,const int &end)
{
if(k==end) return;
if(v(end)<v(k))
update(lc(k),end);
else update(rc(k),end);
s(k)=s(lc(k))+s(rc(k))+c(k);
return;
}
void check(int &k,const int &end)
{
if(k==end) return;
if(!balance(k)) {
rebuild(k);
update(root,k);
return;
}
if(v(end)<v(k)) check(lc(k),end);
else check(rc(k),end);
return;
}
void insert(int &k,const int &key)
{
if(!k) {
k=++pool;
v(k)=key;
lc(k)=rc(k)=0;
c(k)=s(k)=f(k)=1;
check(root,k);
return;
}
++s(k); ++f(k);
if(v(k)==key) ++c(k);
else if(key<v(k))
insert(lc(k),key);
else insert(rc(k),key);
return;
}
void erase(int &k,const int &key)
{
if(v(k)==key&&c(k)) {
--f(k); --c(k);
check(root,k);
return;
}
--f(k);
if(key<v(k)) erase(lc(k),key);
else erase(rc(k),key);
return;
}
inline int getnum(int rank)
{
int now=root;
while(now) {
if(c(now)>0&&f(lc(now))<rank&&f(lc(now))+c(now)>=rank)
return v(now);
if(f(lc(now))>=rank) now=lc(now);
else rank-=f(lc(now))+c(now),now=rc(now);
}
return 0;
}
inline int getrank(const int &key)
{
int now=root,res=1;
while(now) {
if(key==v(now)) return res+f(lc(now));
if(key<v(now)) now=lc(now);
else res+=f(lc(now))+c(now),now=rc(now);
}
return res;
}
inline int find(const int &key)
{
int now=root;
while(now) {
if(v(now)==key) { return c(now) ? now : 0; }
if(key<v(now)) now=lc(now);
else now=rc(now);
}
return now;
}
inline int Query_pre(const int &key)
{
return getnum(getrank(key)-1);
}
inline int Query_suf(const int &key)
{
return getnum(getrank(key)+c(find(key)));
}
int n;
/*
1、插入 x 数;
2、删除 x 数(若有多个相同的数,因只删除一个);
3、查询 x 数的排名(若有多个相同的数,因输出最小的排名);
4、查询排名为 x 的数;
5、求 x 的前趋(前趋定义为小于 x,且最大的数);
6、求 x 的后继(后继定义为大于 x,且最小的数)。
*/
int main()
{
scanf("%d",&n);
int opt,x;
for(int i=1;i<=n;i++) {
scanf("%d%d",&opt,&x);
switch(opt) {
case 1 : insert(root,x); break;
case 2 : erase(root,x); break;
case 3 : printf("%d\n",getrank(x)); break;
case 4 : printf("%d\n",getnum(x)); break;
case 5 : printf("%d\n",Query_pre(x)); break;
case 6 : printf("%d\n",Query_suf(x)); break;
}
}
// getchar();
return 0;
}