P5076 普通二叉树 题解

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

题目描述

您需要写一种数据结构,来维护一些数( 都是 $10^9$ 以内的数字)的集合,最开始时集合是空的。其中需要提供以下操作,操作次数 $q$ 不超过 $10^4$:

  1. 查询 $x$ 数的排名(排名定义为比当前数小的数的个数 $+1$。若有多个相同的数,应输出最小的排名)。

  1. 查询排名为 $x$ 的数。

  1. 求 $x$ 的前驱(前驱定义为小于 $x$,且最大的数)。若未找到则输出 $-2147483647$。

  1. 求 $x$ 的后继(后继定义为大于 $x$,且最小的数)。若未找到则输出 $2147483647$。

  1. 插入一个数 $x$。

输入格式

第一行是一个整数 $q$,表示操作次数。

接下来 $q$ 行,每行两个整数 $op,x$,分别表示操作序号以及操作的参数 $x$。

输出格式

输出有若干行。对于操作 $1,2,3,4$,输出一个整数,表示该操作的结果。

样例 #1

样例输入 #1

7
5 1
5 3
5 5
1 3
2 2
3 3
4 3

样例输出 #1

2
3
1
5

思路

建立一颗二叉树,其中每个节点具有这样的性质:该节点左子树中的元素全部小于该节点,右子树中的全部元素全部大于该节点。每个节点应包含:左儿子left、右儿子right、值value、数量num(同一值可能出现多次)、以该节点为根的二叉树的大小size。

对于操作1,要查找数x的排名,即查找有多少个数比x小,得到的值加1即可。想要查找有多少个数比x小,从根节点开始,如果根节点比x小,说明根节点和它的左子树中的所有元素都比x小,且右子树中仍可能有比x小的数,返回根节点的num+左子树的size+右子树中比x小的数的个数;如果根节点等于x,说明根节点的左子树中所有元素都比x小,根节点和其右子树中均不存在比x小的元素,返回左子树的size;如果根节点比x大,说明根节点和他的右子树中均不存在比x小的数,但左子树中仍可能有比x小的数,返回左子树中比x小的数的个数。

对于操作2,要查找排名为x的数,即要使有x-1个数比x小。对于一个根节点,根节点在这棵树中的排名为左子树的size+1,如果该排名==x,返回根结点的值;如果该排名>x,说明应在左子树中继续查找排名为x的数;如果该排名<x,说明应在右子树中继续查找排名为x-根节点的num-左子树的size的数(注意此处待查找排名的变更)。

对于操作3、4,先查找数x的排名,然后查找排名为x-1或x+1(x+num)的数即可,可直接调用操作2的函数,但要注意一个数出现多次时对排名区间产生的影响。

对于操作5,建树,x比根节点大,放到右子树中;x比根节点小,放到左子树中;x=根节点,根节点num++;当遍历到数的根部,像树中插入该节点即可。

#include<bits/stdc++.h>
using namespace std;
struct Node{
    int left,right,value,size,num;
}t[10010];
int n=0;
void build(int r,int x){//把x放到节点序号r下
    t[r].size++;//上级节点的子树大小++
    if(r==0) return;
    if(x==t[r].value){//找到相等的数,则对应节点num++
        t[r].num++;
        return;
    }
    if(x>t[r].value){//更大,插入到节点的右子树中
        if(t[r].right!=0)build(t[r].right,x);//不为空,继续向下
        else{//右子树为空,放置在右节点上
            t[++n]={0,0,x,1,1};
            t[r].right=n;
        }
        return;
    }
    if(x<t[r].value){
        if(t[r].left!=0)build(t[r].left,x);
        else{
            t[++n]={0,0,x,1,1};
            t[r].left=n;
        }
        return;
    }
}
int find_lower(int r,int x){//在节点r下比x小的数的数量
    if(r==0) return 0;
    if(t[r].value>=x) return find_lower(t[r].left,x);//该节点值已经大于或等于x,只有在其左子树中才可能有小于x的数
    if(t[r].value<x) return find_lower(t[r].left,x)+find_lower(t[r].right,x)+t[r].num;//该节点值小于x,除了该节点本身外,在其左右子树中均可能有小于x的数
}
int find_rank(int r,int rank){//在节点r下寻找 在以该节点为根的子树中 排名为rank的节点序号
    if(rank>t[r].size||rank==0) return 0;
    if(rank>t[t[r].left].size&&rank<=t[t[r].left].size+t[r].num) return r;
    if(t[t[r].left].size+t[r].num<rank) return find_rank(t[r].right,rank-t[t[r].left].size-t[r].num);
    if(t[t[r].left].size+1>rank) return find_rank(t[r].left,rank);
    return 0;
}
int find_value(int r,int x){//在节点r下寻找值为x的节点
    if(t[r].value==x||r==0) return r;
    if(t[r].value<x) return find_value(t[r].right,x);
    if(x<t[r].value) return find_value(t[r].left,x);
}
int main(){
    int Q;
    cin>>Q;
    while(Q--){
        int op,x;
        cin>>op>>x;
        if(op==5){
            if(n==0){
                t[++n]={0,0,x,1,1};
            }
            else{
                build(1,x);
            }
        }
        if(op==1){
            cout<<find_lower(1,x)+1<<endl;
        }
        if(op==2){
            cout<<t[find_rank(1,x)].value<<endl;
        }
        if(op==3){
            int Rank=find_lower(1,x)+1,p;
            p=find_rank(1,Rank-1);
            if(p==0) cout<<"-2147483647\n";
            else cout<<t[p].value<<endl;
        }
        if(op==4){
            int Rank=find_lower(1,x)+1,id=find_value(1,x),p;
            if(id==0) p=find_rank(1,Rank);//没有该数
            else p=find_rank(1,Rank+t[id].num);//有该数
            if(p==0) cout<<"2147483647\n";
            else cout<<t[p].value<<endl;
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值