P3369 【模板】普通平衡树

P3369 【模板】普通平衡树

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入xxx数
  2. 删除xxx数(若有多个相同的数,因只删除一个)
  3. 查询xxx数的排名(排名定义为比当前数小的数的个数+1+1+1。若有多个相同的数,因输出最小的排名)
  4. 查询排名为xxx的数
  5. 求xxx的前驱(前驱定义为小于xxx,且最大的数)
  6. 求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;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值