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;
}

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
平衡树也称为自平衡二叉搜索树,实现起来比较复杂,需要用到旋转操作来保持树的平衡,如左旋、右旋、双旋等。 下面是一份平衡树模板代码,主要包括插入、删除、查找操作,以及各种旋转操作实现。 ```c #include <stdio.h> #include <stdlib.h> // 平衡树节点结构体 typedef struct TreeNode { int val, height; // val:节点权值,height:节点高度 struct TreeNode *left, *right; // 左右子节点指针 } TreeNode; // 获取节点高度 int getHeight(TreeNode *node) { return node ? node->height : 0; } // 获取节点平衡因子 int getBalanceFactor(TreeNode *node) { return getHeight(node->left) - getHeight(node->right); } // 更新节点高度 void updateHeight(TreeNode *node) { node->height = 1 + fmax(getHeight(node->left), getHeight(node->right)); } // 左旋操作 TreeNode *leftRotate(TreeNode *node) { TreeNode *right = node->right; node->right = right->left; right->left = node; updateHeight(node); updateHeight(right); return right; } // 右旋操作 TreeNode *rightRotate(TreeNode *node) { TreeNode *left = node->left; node->left = left->right; left->right = node; updateHeight(node); updateHeight(left); return left; } // 平衡操作(使以node为根的树平衡,返回新根节点) TreeNode *balance(TreeNode *node) { // 更新节点高度 updateHeight(node); // 计算平衡因子 int balanceFactor = getBalanceFactor(node); // 如果平衡因子为2,说明左子树比右子树高 if (balanceFactor > 1) { if (getBalanceFactor(node->left) < 0) { // 如果左子树的右子树比左子树高 node->left = leftRotate(node->left); // 先左旋左子节点 } return rightRotate(node); } // 如果平衡因子为-2,说明右子树比左子树高 else if (balanceFactor < -1) { if (getBalanceFactor(node->right) > 0) { // 如果右子树的左子树比右子树高 node->right = rightRotate(node->right); // 先右旋右子节点 } return leftRotate(node); } // 否则说明当前子树已经平衡,直接返回 return node; } // 插入操作(p为当前子树的根节点,val为要插入的节点权值) TreeNode *insert(TreeNode *p, int val) { if (!p) { // 如果是空节点,说明找到了要插入的位置,新建一个节点 p = (TreeNode *) malloc(sizeof(TreeNode)); p->val = val; p->height = 1; p->left = p->right = NULL; } else if (val < p->val) { // 如果要插入的值小于节点值,向左子树递归 p->left = insert(p->left, val); } else { // 否则向右子树递归 p->right = insert(p->right, val); } return balance(p); // 插入成功后,再进行平衡操作 } // 寻找以p为根的树中权值为val的节点 TreeNode *find(TreeNode *p, int val) { if (!p) { // 如果搜索到空节点,说明未找到 return NULL; } else if (val < p->val) { // 如果要查找的值小于节点值,向左子树递归 return find(p->left, val); } else if (val > p->val) { // 如果要查找的值大于节点值,向右子树递归 return find(p->right, val); } else { // 否则就是要查找的节点 return p; } } // 查找以p为根的树中的最小节点 TreeNode *findMin(TreeNode *p) { while (p->left) { // 不停向左子节点遍历,直到为空 p = p->left; } return p; } // 删除操作(p为当前子树的根节点,val为要删除的节点权值) TreeNode *delete(TreeNode *p, int val) { if (!p) { // 如果搜索到空节点,说明未找到,直接返回 return NULL; } else if (val < p->val) { // 如果要删除的值小于节点值,向左子树递归 p->left = delete(p->left, val); } else if (val > p->val) { // 如果要删除的值大于节点值,向右子树递归 p->right = delete(p->right, val); } else { // 否则就是要删除的节点 if (!p->left && !p->right) { // 如果是叶子节点,直接删除 free(p); return NULL; } else if (p->left && !p->right) { // 如果只有左子节点(或没有子节点),指向左子节点 TreeNode *left = p->left; free(p); return left; } else if (!p->left && p->right) { // 如果只有右子节点,指向右子节点 TreeNode *right = p->right; free(p); return right; } else { // 如果有左、右子节点,用右子节点的最小值代替当前节点,然后再删除右子节点最小值 TreeNode *minRight = findMin(p->right); p->val = minRight->val; p->right = delete(p->right, p->val); } } return balance(p); // 删除成功后,再进行平衡操作 } // 深度优先遍历(先序遍历) void dfs(TreeNode *p) { if (!p) return; printf("%d ", p->val); dfs(p->left); dfs(p->right); } // 主函数 int main() { TreeNode *root = NULL; root = insert(root, 10); root = insert(root, 20); root = insert(root, 30); root = insert(root, 40); root = insert(root, 50); root = delete(root, 30); root = delete(root, 20); root = insert(root, 25); dfs(root); return 0; } ``` 上述代码给出了平衡树节点结构体、获取节点高度、获取节点平衡因子、更新节点高度、左旋、右旋、平衡操作、插入、查找、查找最小节点和删除等常用函数实现。同时,还给出了一个简单的测试代码,用于对平衡树进行测试和验证。 使用平衡树可以大大提高数据插入、删除、查找等操作的效率,所以是实际应用中很常用的数据结构之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值