平衡树——AVL树
二叉搜索树
一种树形结构,满足对于树上的任意一个节点,它的左子树上的任意一个节点的值都不大于这个节点的值,它的右子树上的任意节点的值都不小于这个节点的值。
但是如果是按顺序添加,这个树形结构就会退化为一个链式结构。
为了使得它还是一个树形结构,就需要用到平衡树。
平衡树
一种左右子树平衡的二叉搜索树(左右子树的深度之差小于1)。
AVL树
通过树的旋转来维护平衡树。
树的旋转
1.左旋:原树的根节点的右儿子变成新的根节点,它的右子树依然是它的右子树,它的左子树变成原根节点的右子树,这棵新的以原根节点为根节点的树成为新根节点的左子树。
2.右旋:与左旋正好相反。
1.LL型,即一颗不平衡的二叉搜索树的左子树的深度大于它的右子树的深度,且它左子树的左子树的深度大于它左子树的右子树的深度,操作:对这棵树进行一次右旋。
2.RR型,即一颗不平衡的二叉搜索树的右子树的深度大于它的左子树的深度,且它右子树的右子树的深度大于它右子树的左子树的深度,操作:对这棵树进行一次左旋。
3.LR型,即一颗不平衡的二叉搜索树的左子树的深度大于它的右子树的深度,且它左子树的右子树的深度大于它左子树的左子树的深度,操作:先对这棵树的左子树进行一次左旋,再对这棵树进行一次右旋。
4.RL型,即一颗不平衡的二叉搜索树的右子树的深度大于它的左子树的深度,且它右子树的左子树的深度大于它右子树的右子树的深度,操作:先对这棵树的右子树进行一次右旋,再对这棵树进行一次左旋。
插入
正常二叉搜索树的插入,与一个节点进行比较,小于查左儿子,大于等于查右儿子,直到找到空位插入。回溯时判断一个子树是否平衡,若不平衡则进行树的旋转操作。
删除
删除一共有三种情况:
1.要删除的节点无左子树,也无右子树,直接删除。
2.要删除的节点只有左子树,或只有右子树,直接删除并将左子树(或右子树)变成该节点的子树。
3.要删除的节点既有左子树,又有右子树,找到要删除的节点的前驱节点,把要删除的节点的位置上换成要删除的节点的前驱节点,并把问题换成删除要删除的节点的前驱节点,且此节点一定无右子树。
回溯时也要判断一个子树是否平衡,若不平衡则进行树的旋转操作。
注意:插入时左子树上的任意节点都小于根节点,右子树上的任意节点都大于等于根节点,但在树的旋转操作后,左子树上的节点也可能等于根节点。
代码:
#include<iostream>
#include<cmath>
using namespace std;
int n;
struct node{
int l,r;
int zhi;
int deep;
int zs,ys,sum;
}tree[100010];
int cnt;
int yx(int u){
int t=tree[u].l;
tree[u].l=tree[tree[u].l].r;
tree[u].ys=tree[tree[u].r].sum;
tree[u].zs=tree[tree[u].l].sum;
tree[u].sum=tree[u].ys+tree[u].zs+1;
tree[u].deep=max(tree[tree[u].l].deep,tree[tree[u].r].deep)+1;
tree[t].r=u;
u=t;
return u;
}
int zx(int u){
int t=tree[u].r;
tree[u].r=tree[tree[u].r].l;
tree[u].zs=tree[tree[u].l].sum;
tree[u].ys=tree[tree[u].r].sum;
tree[u].sum=tree[u].ys+tree[u].zs+1;
tree[u].deep=max(tree[tree[u].l].deep,tree[tree[u].r].deep)+1;
tree[t].l=u;
u=t;
return u;
}
int add(int u){
if(tree[cnt].zhi<tree[u].zhi){
if(tree[u].l==0){
tree[u].l=cnt;
tree[u].deep=max(tree[tree[u].l].deep,tree[tree[u].r].deep)+1;
tree[u].ys=tree[tree[u].r].sum;
tree[u].zs=1;
tree[u].sum=tree[u].zs+tree[u].ys+1;
return u;
}
tree[u].l=add(tree[u].l);
}
else{
if(tree[u].r==0){
tree[u].r=cnt;
tree[u].deep=max(tree[tree[u].l].deep,tree[tree[u].r].deep)+1;
tree[u].zs=tree[tree[u].l].sum;
tree[u].ys=1;
tree[u].sum=tree[u].ys+tree[u].zs+1;
return u;
}
tree[u].r=add(tree[u].r);
}
if(abs(tree[tree[u].l].deep-tree[tree[u].r].deep)>1){
if(tree[tree[u].l].deep>tree[tree[u].r].deep){
if(tree[tree[tree[u].l].l].deep>tree[tree[tree[u].l].r].deep){
u=yx(u);
}
else{
tree[u].l=zx(tree[u].l);
u=yx(u);
}
}
else{
if(tree[tree[tree[u].r].r].deep>tree[tree[tree[u].r].l].deep){
u=zx(u);
}
else{
tree[u].r=yx(tree[u].r);
u=zx(u);
}
}
}
tree[u].deep=max(tree[tree[u].l].deep,tree[tree[u].r].deep)+1;
tree[u].ys=tree[tree[u].r].sum;
tree[u].zs=tree[tree[u].l].sum;
tree[u].sum=tree[u].zs+tree[u].ys+1;
return u;
}
int gen=0;
int cun=0;
int del(int u,int x){
if(tree[u].zhi==x){
if(tree[u].l==0&&tree[u].r==0){
return 0;
}
if(tree[u].l==0){
return tree[u].r;
}
if(tree[u].r==0){
return tree[u].l;
}
cun=u;
}
if(tree[u].zhi>=x){
tree[u].l=del(tree[u].l,x);
tree[u].ys=tree[tree[u].r].sum;
tree[u].zs=tree[tree[u].l].sum;
tree[u].sum=tree[u].zs+tree[u].ys+1;
}
else{
if(tree[u].r==0){
if(cun!=0){
tree[cun].zhi=tree[u].zhi;
return tree[u].l;
}
}
else{
tree[u].r=del(tree[u].r,x);
tree[u].zs=tree[tree[u].l].sum;
tree[u].ys=tree[tree[u].r].sum;
tree[u].sum=tree[u].zs+tree[u].ys+1;
}
}
if(abs(tree[tree[u].l].deep-tree[tree[u].r].deep)>1){
if(tree[tree[u].l].deep>tree[tree[u].r].deep){
if(tree[tree[tree[u].l].l].deep>tree[tree[tree[u].l].r].deep){
u=yx(u);
}
else{
tree[u].l=zx(tree[u].l);
u=yx(u);
}
}
else{
if(tree[tree[tree[u].r].r].deep>tree[tree[tree[u].r].l].deep){
u=zx(u);
}
else{
tree[u].r=yx(tree[u].r);
u=zx(u);
}
}
}
tree[u].deep=max(tree[tree[u].l].deep,tree[tree[u].r].deep)+1;
tree[u].ys=tree[tree[u].r].sum;
tree[u].zs=tree[tree[u].l].sum;
tree[u].sum=tree[u].zs+tree[u].ys+1;
return u;
}
int pai(int u,int x){
if(x==0){
return 0;
}
if(tree[u].zhi>=x){
if(tree[u].l==0){
return 0;
}
return pai(tree[u].l,x);
}
if(tree[u].r==0){
return tree[u].zs+1;
}
return tree[u].zs+1+pai(tree[u].r,x);
}
int shu(int u,int x){
if(x==tree[u].zs+1){
return tree[u].zhi;
}
if(x<=tree[u].zs){
return shu(tree[u].l,x);
}
return shu(tree[u].r,x-tree[u].zs-1);
}
int main(){
cin>>n;
while(n--){
int opt,x;
cin>>opt>>x;
if(opt==1){
cnt++;
tree[cnt].zhi=x;
tree[cnt].deep=1;
tree[cnt].sum=1;
if(gen!=0){
gen=add(gen);
}
else{
gen=cnt;
}
}
if(opt==2){
gen=del(gen,x);
}
if(opt==3){
cout<<pai(gen,x)+1<<endl;
}
if(opt==4){
cout<<shu(gen,x)<<endl;
}
if(opt==5){
cout<<shu(gen,pai(gen,x))<<endl;
}
if(opt==6){
cout<<shu(gen,pai(gen,x+1)+1)<<endl;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话