BST——二叉搜索树

闲话

P3369 【模板】普通平衡树
会 TLE 一个点,过不了。
但是可以过P5076 【深基16.例7】普通二叉树(简化版)虽然只是道绿,但是可以水一下。

二叉搜索树

首先它得是一颗二叉树。
建立的过程:一个值 x x x 加进来,大于当前值 n o w now now,就往右子树走,小于当前值 n o w now now,就往左子树走,否则 x x x 就是当前的点,点的数量加一。
这样建树可以满足一个性质:

  • 一个点的左子树中所有的点都小于它,右子树中的所有点都大于它,所以一颗二叉搜索树的中序遍历是升序的。

把一颗二叉搜索树建立成这样:

struct node{
	int l,r,cnt,ans,flag;
	int lans;
}f[N];

l , r l,r l,r 是左右儿子编号, c n t cnt cnt 是当前元素数量, a n s ans ans 是元素值, f l a g flag flag 表示这个点存不存在, l a n s lans lans 是左子树大小。

插入

同上。

void add(int p,int x){
	if(!f[p].flag){
		f[++cnt]=node{0,0,1,x,1,0};
		return;
	}
	if(x>f[p].ans){
		if(f[f[p].r].flag){
			add(f[p].r,x);
		}
		else{
			f[++cnt]=node{0,0,1,x,1,0};
			f[p].r=cnt;
		}
	}	
	else if(x<f[p].ans){
		f[p].lans++;
		if(f[f[p].l].flag){
			add(f[p].l,x);
		}
		else{
			f[++cnt]=node{0,0,1,x,1,0};
			f[p].l=cnt;
		}
	}
	else f[p].cnt++;
}

删除

如果当前的值有多个,即 c n t > 1 cnt>1 cnt>1,将 c n t cnt cnt 减一即可。
如果当前点只有一个儿子,那么把这个儿子提上来即可。
重点放在如果有两个儿子的情况。

红色的点是删除的点。
现在它空了,需要找一个点替上。
可以考虑直接上提左子树,再将右子树接在左子树最右端的点上,二叉搜索树性质不变。
但是这样太麻烦了,考虑不移动原来树上的点。
可以考虑在删除点的位置加一个点,因为要维护二叉搜索树中序遍历升序的性质,这个点的值可以使左子树中的最大值或者右子树中的最小值。
现在令将点 x x x 移动到空缺的位置,由于这里有一个 x x x,那么原来的位置需要删除。
如果把原来的点 x x x f [ x ] . c n t f[x].cnt f[x].cnt 个元素全部移动过来,原来的位置就需要删除 f [ x ] . c n t f[x].cnt f[x].cnt 次,还是费时间。
考虑只用点 x x x 占个位置,这样只用删除一次原点。

int getmin(int p){
	if(f[f[p].l].flag)return getmin(f[p].l);
	return p;
}
bool remove(int p,int x){
	if(!f[p].flag)return 0;
	if(f[p].ans==x){
		if(f[p].cnt>1)f[p].cnt--;//如果有多个,直接删
		else{
			if(f[f[p].l].flag&&f[f[p].r].flag){//这里选择的是提右子树中的最小值
				int q=getmin(f[p].r);
				f[p].ans=f[q].ans;
				remove(f[p].r,f[q].ans);
			}
			else if(f[f[p].l].flag)f[p]=f[f[p].l];//有任意一个,直接提上
			else if(f[f[p].r].flag)f[p]=f[f[p].r];
			else f[p].flag=0;//叶子结点直接删
		}
		return 1;
	}
	else if(x>f[p].ans){
		return remove(f[p].r,x);
	}
	else{
		bool glaf=remove(f[p].l,x);//如果删除了点才将 lans 减一
		if(glaf)f[p].lans--;
		return glaf;
	}
}

前驱

x x x 的前驱:序列中小于 x x x 的元素中的最大值。
二叉搜索树的性质:左子树中的值都小于当前值。
所以遇到大于等于查询值 x x x 的就往左子树走。
如果遇到 f [ p ] . a n s < x f[p].ans<x f[p].ans<x,那么记录 p p p,然后往右子树走。(因为前驱需要尽可能大)

void pre(int p,int x){
	if(!f[p].flag)return;
	if(f[p].ans<x){
		Pre=f[p].ans;
		pre(f[p].r,x);
	}
	else{
		pre(f[p].l,x);
	}
}

后继

x x x 的后继:序列中大于 x x x 的元素中的最小值。
二叉搜索树的性质:右子树中的值都大于当前值。
所以遇到小于等于查询值 x x x 的就往右子树走。
如果遇到 f [ p ] . a n s > x f[p].ans>x f[p].ans>x,那么记录 p p p,然后往左子树走。(因为后继需要尽可能小)

void suc(int p,int x){
	if(!f[p].flag)return;
	if(f[p].ans>x){
		Suc=f[p].ans;
		suc(f[p].l,x);
	}
	else{
		suc(f[p].r,x);
	}
}

查排名

x x x 的排名:将序列升序排序后,第一个值为 x x x 的数的位置。
这时候 l a n s lans lans 就有用了。
l a n s lans lans 表示左子树的大小,也就是表示序列中有多少元素小于 f [ p ] . a n s f[p].ans f[p].ans
找到一个点 p p p 时,若 f [ p ] . a n s > x f[p].ans>x f[p].ans>x,对于排名无贡献,往左子树走。
f [ p ] . a n s = x f[p].ans=x f[p].ans=x,此时的贡献就是 f [ p ] . l a n s + 1 f[p].lans+1 f[p].lans+1(因为是排名,所以要加一)
f [ p ] . a n s < x f[p].ans<x f[p].ans<x,说明当前点 p p p 以及 p p p 的左子树中的所有点都比 x x x 小,对排名的贡献为 f [ p ] . l a n s + f [ p ] . l a n s f[p].lans+f[p].lans f[p].lans+f[p].lans
图:
在这里插入图片描述

int ranking(int p,int x){
	if(!f[p].flag){
		return 1;
	}
	if(f[p].ans==x){
		return f[p].lans+1;
	}
	else if(x>f[p].ans){
		return f[p].lans+f[p].cnt+ranking(f[p].r,x);
	}
	else{
		return ranking(f[p].l,x);
	}
}

根据排名查询数

查询排名为 x x x 的数,就是查询大于等于 x − 1 x-1 x1 个数的元素。
利用 l a n s lans lans,把排名查询反过来就行了。

int query(int p,int x){
	if(f[p].lans+f[p].cnt<=x){
		return query(f[p].r,x-f[p].lans-f[p].cnt);
	}
	else if(f[p].lans<=x){
		return f[p].ans;
	}
	return query(f[p].l,x);
}

P3369 【模板】普通平衡树

会 TLE,只有 91 分。

#include<bits/stdc++.h>
#define int long long
#define endl putchar('\n')
#define psp putchar(' ')
using namespace std;
const int N=1e6+5;
int read(){
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}
void print(int x){
	if(x<0)putchar('-'),x=-x;
	if(x<10){putchar(x+'0');return;}
	print(x/10);
	putchar(x%10+'0');
}
int m;
int cnt;
struct node{
	int l,r,cnt,ans,flag;
	int lans;
}f[N];
int Pre;
int Suc;
int now;
void add(int p,int x){
	if(!f[p].flag){
		f[++cnt]=node{0,0,1,x,1,0};
		return;
	}
	if(x>f[p].ans){
		if(f[f[p].r].flag){
			add(f[p].r,x);
		}
		else{
			f[++cnt]=node{0,0,1,x,1,0};
			f[p].r=cnt;
		}
	}	
	else if(x<f[p].ans){
		f[p].lans++;
		if(f[f[p].l].flag){
			add(f[p].l,x);
		}
		else{
			f[++cnt]=node{0,0,1,x,1,0};
			f[p].l=cnt;
		}
	}
	else f[p].cnt++;
}
int getmin(int p){
	if(f[f[p].l].flag)return getmin(f[p].l);
	return p;
}
bool remove(int p,int x){
	if(!f[p].flag)return 0;
	if(f[p].ans==x){
		if(f[p].cnt>1)f[p].cnt--;//如果有多个,直接删
		else{
			if(f[f[p].l].flag&&f[f[p].r].flag){//这里选择的是提右子树中的最小值
				int q=getmin(f[p].r);
				f[p].ans=f[q].ans;
				remove(f[p].r,f[q].ans);
			}
			else if(f[f[p].l].flag)f[p]=f[f[p].l];//有任意一个,直接提上
			else if(f[f[p].r].flag)f[p]=f[f[p].r];
			else f[p].flag=0;//叶子结点直接删
		}
		return 1;
	}
	else if(x>f[p].ans){
		return remove(f[p].r,x);
	}
	else{
		bool glaf=remove(f[p].l,x);//如果删除了点才将 lans 减一
		if(glaf)f[p].lans--;
		return glaf;
	}
}
int ranking(int p,int x){
	if(!f[p].flag){
		return 1;
	}
	if(f[p].ans==x){
		return f[p].lans+1;
	}
	else if(x>f[p].ans){
		return f[p].lans+f[p].cnt+ranking(f[p].r,x);
	}
	else{
		return ranking(f[p].l,x);
	}
}
int query(int p,int x){
	if(f[p].lans+f[p].cnt<=x){
		return query(f[p].r,x-f[p].lans-f[p].cnt);
	}
	else if(f[p].lans<=x){
		return f[p].ans;
	}
	return query(f[p].l,x);
}
void pre(int p,int x){
	if(!f[p].flag)return;
	if(f[p].ans<x){
		Pre=f[p].ans;
		pre(f[p].r,x);
	}
	else{
		pre(f[p].l,x);
	}
}
void suc(int p,int x){
	if(!f[p].flag)return;
	if(f[p].ans>x){
		Suc=f[p].ans;
		suc(f[p].l,x);
	}
	else{
		suc(f[p].r,x);
	}
}
signed main(){
	m=read();
	while(m--){
		int op=read(),x=read();
		if(op==1){
			add(1,x);
			now++;
		}
		else if(op==2){
			if(remove(1,x))now--;
			if(now==0)cnt=0;
		}
		else if(op==3){
			print(ranking(1,x)),endl;
		}
		else if(op==4){
			print(query(1,x-1)),endl;
		}
		else if(op==5){
			pre(1,x);
			print(Pre),endl;
		}
		else{
			suc(1,x);
			print(Suc),endl;
		}
	}
}

P5076 【深基16.例7】普通二叉树(简化版)

#include<bits/stdc++.h>
#define int long long
#define endl putchar('\n')
#define psp putchar(' ')
using namespace std;
const int N=1e6+5;
int read(){
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}
void print(int x){
	if(x<0)putchar('-'),x=-x;
	if(x<10){putchar(x+'0');return;}
	print(x/10);
	putchar(x%10+'0');
}
int m;
int cnt;
struct node{
	int l,r,cnt,ans,flag;
	int lans;
}f[N];
int Pre;
int Suc;
int now;
void add(int p,int x){
	if(!f[p].flag){
		f[++cnt]=node{0,0,1,x,1,0};
		return;
	}
	if(x>f[p].ans){
		if(f[f[p].r].flag){
			add(f[p].r,x);
		}
		else{
			f[++cnt]=node{0,0,1,x,1,0};
			f[p].r=cnt;
		}
	}	
	else if(x<f[p].ans){
		f[p].lans++;
		if(f[f[p].l].flag){
			add(f[p].l,x);
		}
		else{
			f[++cnt]=node{0,0,1,x,1,0};
			f[p].l=cnt;
		}
	}
	else f[p].cnt++;
}
int getmin(int p){
	if(f[f[p].l].flag)return getmin(f[p].l);
	return p;
}
bool remove(int p,int x){
	if(!f[p].flag)return 0;
	if(f[p].ans==x){
		if(f[p].cnt>1)f[p].cnt--;
		else{
			if(f[f[p].l].flag&&f[f[p].r].flag){
				int q=getmin(f[p].r);
				f[p].ans=f[q].ans;
				remove(f[p].r,f[q].ans);
			}
			else if(f[f[p].l].flag)f[p]=f[f[p].l];
			else if(f[f[p].r].flag)f[p]=f[f[p].r];
			else f[p].flag=0;
		}
		return 1;
	}
	else if(x>f[p].ans){
		return remove(f[p].r,x);
	}
	else{
		bool glaf=remove(f[p].l,x);
		if(glaf)f[p].lans--;
		return glaf;
	}
}
int ranking(int p,int x){
	if(!f[p].flag){
		return 1;
	}
	if(f[p].ans==x){
		return f[p].lans+1;
	}
	else if(x>f[p].ans){
		return f[p].lans+f[p].cnt+ranking(f[p].r,x);
	}
	else{
		return ranking(f[p].l,x);
	}
}
int query(int p,int x){
	if(f[p].lans+f[p].cnt<=x){
		return query(f[p].r,x-f[p].lans-f[p].cnt);
	}
	else if(f[p].lans<=x){
		return f[p].ans;
	}
	return query(f[p].l,x);
}
void pre(int p,int x){
	if(!f[p].flag)return;
	if(f[p].ans<x){
		Pre=f[p].ans;
		pre(f[p].r,x);
	}
	else{
		pre(f[p].l,x);
	}
}
void suc(int p,int x){
	if(!f[p].flag)return;
	if(f[p].ans>x){
		Suc=f[p].ans;
		suc(f[p].l,x);
	}
	else{
		suc(f[p].r,x);
	}
}
signed main(){
	m=read();
	while(m--){
		int op=read(),x=read();
		if(op==5){
			add(1,x);
			now++;
		}
		else if(op==1){
			print(ranking(1,x)),endl;
		}
		else if(op==2){
			print(query(1,x-1)),endl;
		}
		else if(op==3){
			Pre=-2147483647;
			pre(1,x);
			print(Pre),endl;
		}
		else{
			Suc=2147483647;
			suc(1,x);
			print(Suc),endl;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值