P3369 【模板】普通平衡树
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
- 插入xxx数
- 删除xxx数(若有多个相同的数,因只删除一个)
- 查询xxx数的排名(排名定义为比当前数小的数的个数+1+1+1。若有多个相同的数,因输出最小的排名)
- 查询排名为xxx的数
- 求xxx的前驱(前驱定义为小于xxx,且最大的数)
- 求xxx的后继(后继定义为大于xxx,且最小的数)
输入输出格式
输入格式:
第一行为nnn,表示操作的个数,下面nnn行每行有两个数optoptopt和xxx,optoptopt表示操作的序号( 1≤opt≤6 1 \leq opt \leq 6 1≤opt≤6 )
输出格式:
对于操作3,4,5,63,4,5,63,4,5,6每行输出一个数,表示对应答案
输入输出样例
输入样例#1: 复制
10 1 106465 4 1 1 317721 1 460929 1 644985 1 84185 1 89851 6 81968 1 492737 5 493598
输出样例#1: 复制
106465 84185 492737
说明
时空限制:1000ms,128M
1.n的数据范围: n≤100000 n \leq 100000 n≤100000
2.每个数的数据范围: [−107,107][-{10}^7, {10}^7][−107,107]
来源:Tyvj1728 原名:普通平衡树
https://www.luogu.org/problemnew/show/P3369
https://www.cnblogs.com/ppprseter/p/9382132.html
自己在网上找了一个,但是没有实现3 4 功能
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
typedef struct AVLNode *Tree;
typedef int ElementType;
struct AVLNode
{
int depth; //深度,这里计算每个结点的深度,通过深度的比较可得出是否平衡
Tree parent; //该结点的父节点,方便操作
ElementType val; //结点值
Tree lchild;
Tree rchild;
AVLNode(int val=0) //默认构造函数
{
parent=NULL;
depth=0;
lchild=rchild=NULL;
this->val=val;
}
};
Tree insert_val(Tree&,Tree,Tree);
Tree remove(Tree&,ElementType);
Tree remove_val(Tree &,Tree &);
void update_depth(Tree);
int get_balance(Tree);
int is_balance(Tree);
Tree *Find_Min(Tree&);
Tree connect34(Tree&,Tree&,Tree&,Tree&,Tree&,Tree&,Tree&);
Tree rotateAt(Tree&,Tree&);
void setchild(Tree &,Tree &,Tree &);
//向AVL树中插入val
//参数:根,插入数据value
//返回:新根结点
Tree Insert(Tree &root,ElementType val)
{
Tree temp=NULL;
Tree node=new AVLNode(val);
//插入结点
temp=insert_val(root,node,NULL); //调用真正的插入函数
if (temp)
{
update_depth(temp);
root=rotateAt(root, temp);//检查树是否该调整
}
else //无需插入,释放结点
delete temp;
return root;
}
//插入函数
//参数:根节点,待插结点,待插结点的父节点
//返回:插入结点
Tree insert_val(Tree &root,Tree node,Tree parent)
{
if (root==NULL)
{
root=node;
node->parent=parent; //设置当前结点的父结点
return root; //返回插入结点
}
if (node->val<root->val) //插左子树
return insert_val(root->lchild, node,root);
else if(node->val>root->val) //插右子树
return insert_val(root->rchild, node,root);
else //已存在该结点,停止插入操作,返回NULL
return NULL;
}
//3+4重构函数
//参数:见分析
//返回:新根
Tree connect34(Tree &a,Tree &b,Tree &c,Tree &T0,Tree &T1,Tree &T2,Tree &T3)
{
a->lchild=T0;
if (T0)
T0->parent=a;
a->rchild=T1;
if(T1)
T1->parent=a;
update_depth(a);
c->lchild=T2;
if(T2)
T2->parent=c;
c->rchild=T3;
if(T3)
T3->parent=c;
update_depth(c);
b->lchild=a;
a->parent=b;
b->rchild=c;
c->parent=b;
update_depth(b);
return b;
}
Tree rotateAt(Tree &root,Tree &node)
{
Tree son,temp;
Tree grandson;
int balance=0; //平衡因子
while (node!=NULL) //检查其祖先是否需要调整,更新
{
update_depth(node); //更新当前结点的高度信息
balance=is_balance(node); //获取当前结点的平衡因子情况
if (balance>1 || balance<-1) //平衡因子超标
{
if (balance>1) //左子树高
{
if (is_balance(node->lchild)>0) //LL型
{
//找祖孙三代,后面的类似
son=node->lchild; //找其左孩子
grandson=son->lchild; //找其左孩子的左孩子
son->parent=node->parent; //设置更新后的son的父节点
temp=node;
//重构
node=connect34(grandson, son, node, grandson->lchild, grandson->rchild, son->rchild, node->rchild);
setchild(son, temp, node);//设置son父节点的孩子为node
}
else //LR型
{
son=node->lchild;
grandson=son->rchild;
grandson->parent=node->parent;
temp=node;
node=connect34(son, grandson, node, son->lchild, grandson->lchild, grandson->rchild, node->rchild);
setchild(grandson, temp, node); //设置grandson父节点的孩子为node
}
}
else //右子树高
{
if (is_balance(node->rchild)<0) //RR型
{
son=node->rchild;
grandson=son->rchild;
son->parent=node->parent;
temp=node;
node=connect34(node, son, grandson, node->lchild, son->lchild, grandson->lchild, grandson->rchild);
setchild(son, temp, node); //设置son父节点的孩子为node
}
else //RL型
{
son=node->rchild;
grandson=son->lchild;
grandson->parent=node->parent;
temp=node;
node=connect34(node, grandson, son, node->lchild, grandson->lchild, grandson->rchild, son->rchild);
setchild(grandson, temp, node); //设置grandson父节点的孩子为node
}
}
if (node->parent==NULL) //到达根结点
{
root=node; //设置新的根结点
break; //退出
}
}
node=node->parent; //依次找到其父节点
}
return root; //返回新根
}
void setchild(Tree &g,Tree &temp,Tree &node)
{
if (g->parent)
{
if (g->parent->lchild==temp)
g->parent->lchild=node;
else
g->parent->rchild=node;
}
}
//查找最小结点
Tree *Find_Min(Tree &root)
{
if (root->lchild)
{
return Find_Min(root->lchild);
}
return &root;
}
//删除操作
//参数:根,需要删除的结点
//返回值: 返回删除结点的父节点
Tree remove_val(Tree &root,Tree &node)
{
Tree parent=node->parent;
Tree temp=NULL;
//只有左孩子
if (node->rchild==NULL && node->lchild!=NULL)
{
temp=node;
node=node->lchild; //指向左孩子
node->parent=temp->parent;
delete temp; //释放结点
update_depth(node); //更新当前结点信息
}
else if(node->lchild==NULL && node->rchild!=NULL) //只有右孩子
{
temp=node;
node=node->rchild; //指向右结点
node->parent=temp->parent;
delete temp; //释放结点
update_depth(node); //更新当前结点信息
}
else if(node->rchild==NULL && node->lchild==NULL) //叶子结点
{
parent=node->parent; //找到其父节点
if (parent) //如果父节点存在
{
delete node;
node=NULL;
update_depth(parent); //更新父节点高度信息
}
else //删除的是根
{
delete root;
root=NULL;
}
}
else //既有左孩子也有右孩子,化繁为简
{
Tree *tmp=Find_Min(node->rchild); //找到替代元素,temp为叶子结点
node->val=(*tmp)->val; //更新值
//判断当前叶子结点是左孩子还是右孩子。
parent=(*tmp)->parent;
delete *tmp;
*tmp=NULL;
update_depth(parent);
}
return parent;
}
//找到删除的结点,执行删除操作,并根据情况调整AVL树
//参数:根,需要删除的val
//返回:找到删除结点的情况则返回新根,否则返回NULL
Tree remove(Tree &root,ElementType val)
{
static Tree *temp=NULL;
if (root==NULL)
{
temp=NULL;
return NULL;
}
else if(root->val<val) //在右子树查找
remove(root->rchild, val);
else if(root->val>val) //在左子树查找
remove(root->lchild, val);
else //找到了,标记一下
temp=&root;
if (temp)
{
if (!root->parent) //如果已经返回到最后一次(也就是root是真正的树根)
{
Tree tmp=NULL;
tmp=remove_val(root,*temp); //执行删除操作
return rotateAt(root, tmp);
}
return *temp;
}
return NULL;
}
//获取当前结点的深度
int get_balance(Tree node)
{
if (node==NULL)
return 0;
return node->depth;
}
//返回当前平衡因子
int is_balance(Tree node)
{
if (node==NULL)
return 0;
else
return get_balance(node->lchild)-get_balance(node->rchild);
}
//更新当前深度
void update_depth(Tree node)
{
if (node==NULL)
return;
else
{
int depth_Lchild=get_balance(node->lchild); //左孩子深度
int depth_Rchild=get_balance(node->rchild); //右孩子深度
node->depth=max(depth_Lchild,depth_Rchild)+1;
}
}
//前序
void PreOrder(Tree root)
{
if (root==NULL)
return;
printf("%d ",root->val);
PreOrder(root->lchild);
PreOrder(root->rchild);
}
//中序
void InOrder(Tree root)
{
if (root==NULL)
return;
InOrder(root->lchild);
printf("%d ",root->val);
InOrder(root->rchild);
}
Tree getrightmost(Tree heap)//这个函数是求左子树中最右的节点的函数
{
while (heap!=NULL)
heap=heap->rchild;
return heap->parent;
}
Tree getPrenode(Tree heap)//heap代表当前要找前驱节点的节点
{
if(heap==NULL)//如果当前节点为空,返回空
return heap;
if(heap->lchild!=NULL)//如果当前节点左子树不为空,那么该节点的后继节点为左子树中最右的节点
return getrightmost(heap->lchild);//找到左子数树中最右的节点,并返回
else //如果程序到达这里,说明当前节点的左子数为空
{
Tree parent=heap->parent;
while (parent!=NULL&&heap!=parent->rchild)//第一个条件就是判断那种特殊情况的,一直延伸到当前节点没有父亲节点时,那么parent为空,这个条件不满足,函数返回空
{ //第二个条件是判断当前节点是否为该父亲节点的右孩子,如果不是,继续执行下面的代码,如果是,说明这个父亲节点就是heap节点的前驱节点
heap=parent;
parent=heap->parent;
}
return parent;
}
}
Tree getleftmost(Tree heap)//这个函数是求右子树中最左的节点的函数
{
while (heap!=NULL)
heap=heap->lchild;
return heap->parent;
}
Tree getsuccessornode(Tree heap)//heap代表当前要找后继节点的节点
{
if(heap==NULL)//如果当前节点为空,返回空
return heap;
if(heap->rchild!=NULL)//如果当前节点右子树不为空,那么该节点的后继节点为右子树中最左的节点
return getleftmost(heap->rchild);//找到右子数树中最左的节点,并返回
else //如果程序到达这里,说明当前节点的右子数为空
{
Tree parent=heap->parent;
while (parent!=NULL&&heap!=parent->lchild)//第一个条件就是判断那种特殊情况的,一直延伸到当前节点没有父亲节点时,那么parent为空,这个条件不满足,函数返回空
{ //第二个条件是判断当前节点是否为该父亲节点的左孩子,如果不是,继续执行下面的代码,如果是,说明这个父亲节点就是heap节点的后继节点
heap=parent;
parent=heap->parent;
}
return parent;
}
}
Tree Find(Tree T,int x){
while(T != NULL){
if(T->val == x){
return T;
}else
if(x > T->val){
T = T->rchild;
}else{
T = T->lchild;
}
}
return T;
}
int last;
void findMaxMin(int aim, Tree ptr)
{
if(ptr!=NULL)
{
findMaxMin(aim, ptr->lchild);
if(last<aim && ptr->val>=aim) //找到小于aim的最大元素
printf("a=%d\n",last);
if(last<=aim && ptr->val>aim) //找到大于aim的最小元素
printf("b=%d\n",ptr->val);
last=ptr->val;
findMaxMin(aim, ptr->rchild);
}
}
int main()
{
Tree root=NULL;
int n;
cin>>n;
cout<<"--------------"<<endl;
for(int i=0;i<n;i++)
{
int a,b;
cin>>a>>b;
switch(a)
{
case 1:
root = Insert(root, b);
break;
case 2:
remove(root, b);
break;
case 5:
findMaxMin(493598,root);
Tree t=getPrenode(Find(root,b));
if(t!=NULL)
cout<<t->val<<endl;
break;
case 6:
findMaxMin(81968,root);
t=getsuccessornode(Find(root,b));
if(t!=NULL)
cout<<t->val<<endl;
break;
default:
break;
}
}
cout<<"--------------"<<endl;
printf("前序:");
PreOrder(root);
printf("\n");
printf("中序:");
InOrder(root);
printf("\n");
return 0;
}