替罪羊树学习小记

替罪羊树是一种没有旋转操作的二叉搜索树。

或者说就是一颗二叉搜索树,暴力的那种。

插入就是自上而下找到应该插入的地方而后插进去。

我们知道这样搞肯定是可以变成 O ( n ) O(n) O(n)的深度

其复杂度的保证依赖于重构。

即一棵子树不平衡直接提出来重构成完全二叉树。

不平衡的定义为:
m a x ( s i z [ x 0 ] , s i z [ x 1 ] ) > s i z [ x ] ∗ α max(siz[x0],siz[x1])>siz[x]*\alpha max(siz[x0],siz[x1])>siz[x]α
α \alpha α一般取 0.75 0.75 0.75

没有旋转的话删除会比较麻烦,需要找到左子树最右的点或右子树最左的点来替代,再把那个点删掉,所以这叫替罪羊树。

复杂度 O ( n l o g n ) O(nlogn) O(nlogn),天知道为什么

Code:

#include<cstdio>
#include<iostream>
#define al 0.75
#define x0 t[x][0]
#define x1 t[x][1]
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;

const int N = 1e5 + 5;

int Q, n, rt, op, x, t[N][2], siz[N], a[N], fa[N];
int d[N], d0;

int lr(int x) {return t[fa[x]][1] == x;}
void upd(int x) { siz[x] = siz[x0] + siz[x1] + 1;}

void ins(int &x, int k) {
	if(!x) { x = k; return;}
    int z = a[k] > a[x];
    ins(t[x][z], k); fa[t[x][z]] = x;
	upd(x);
}
int pd(int x) {return max(siz[x0], siz[x1]) > siz[x] * al;}
void rec(int x) {
	if(!x) return;
	rec(x0); d[++ d0] = x; rec(x1);
}
int dg(int l, int r) {
	if(l > r) return 0;
	int m = l + r >> 1, x = d[m];
	fa[t[x][0] = dg(l, m - 1)] = x;
	fa[t[x][1] = dg(m + 1, r)] = x;
	upd(x); return x;
}
int ft(int x, int k) {return a[x] == k ? x : ft(t[x][k > a[x]], k);}
int fr(int x) { return x1 ? fr(x1) : x;}
int fl(int x) { return x0 ? fl(x0) : x;}
void del(int x) {
	if(x0 && x1) {
		int p = x0;
		while(t[p][1]) p = t[p][1];
		a[x] = a[p]; x = p;
	}
	int p = x0 ? x0 : x1, y = fa[x], z = lr(x);
	fa[t[y][z] = p] = y;
	for(x = y; x; x = fa[x]) upd(x);
	if(x == rt) rt = p;
}

int gn(int x, int k) {
	if(!x) return 0;
	if(a[x] >= k) return gn(x0, k);
	return siz[x0] + 1 + gn(x1, k);
}
int fn(int x, int k) {
	if(siz[x0] + 1 == k) return x;
	if(siz[x0] >= k) return fn(x0, k);
	return fn(x1, k - siz[x0] - 1);
}
int gl(int x, int k) {
	if(!x) return 0;
	if(a[x] >= k) return gl(x0, k);
	int p = gl(x1, k);
	return p ? p : x;
}
int gr(int x, int k) {
	if(!x) return 0;
	if(a[x] <= k) return gr(x1, k);
	int p = gr(x0, k);
	return p ? p : x;
}

const int inf = 2147483647;

int main() {
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
    cin >> Q;
    
    a[++ n] = -inf; ins(rt, n);
    a[++ n] = inf; ins(rt, n);
    
    fo(ii, 1, Q) {
        scanf("%d %d", &op, &x);
        if(op == 1) {
            a[++ n] = x; siz[n] = 1;
            ins(rt, n);
            int cur = 0;
            x = n;
            for(; x; x = fa[x]) if(pd(x)) cur = x;
            if(cur) {
            	x = cur; d0 = 0, rec(x);
            	int y = fa[x], z = lr(x);
            	x = dg(1, d0);
            	fa[t[y][z] = x] = y;
            	if(rt == cur) rt = x;
			}
        }
        if(op == 2) del(ft(rt, x));
        if(op == 3) printf("%d\n", gn(rt, x));
        if(op == 4) printf("%d\n", a[fn(rt, x + 1)]);
        if(op == 5) printf("%d\n", a[gl(rt, x)]);
        if(op == 6) printf("%d\n", a[gr(rt, x)]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值