【洛谷P5076】普通二叉树(简化)

今天接着学二叉树

说实在话当我看到这题的时候
诶?这给我干哪儿来了 这还是国内吗
然后就翻书学了一种新的数据结构 二叉查找树(又称二叉搜索树)
当然这个用理解记忆就可以了 后面会用平衡树进行完善
因为二叉查找树在退化成链的时候可能会变成O(\(n^2\))
我们先看一下题目

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

题目描述

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

  1. 定义数 \(x\) 的排名为集合中小于 \(x\) 的数的个数 \(+1\)。查询数 \(x\) 的排名。注意 \(x\) 不一定在集合里
  2. 查询排名为 \(x(x\ge 1)\) 的数。保证集合里至少有 \(x\) 个数
  3. \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)。若不存在则输出 \(-2147483647\)
  4. \(x\) 的后继(后继定义为大于 \(x\),且最小的数)。若不存在则输出 \(2147483647\)
  5. 插入一个数 \(x\),本题的数据保证插入前 \(x\) 不在集合中。

保证执行 \(1,3,4\) 操作时,集合中有至少一个元素。

输入格式

第一行是一个整数 \(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

解法&&个人感想

现在解释一下二叉查找树的定义:
这棵树满足以下性质:
1.对任意节点,若左子树不空,则左子树的所有值均小于根节点的值
2.对任意节点,若右子树不空,则右子树的所有值均大于根节点的值
3.不存在值相同的节点
由此观之,二叉查找树的中序遍历是单调递增序列
对于一个二叉查找树,我们有以下定义:

struct node{
    int l,r,siz,cnt,val;
};
node tree[1000005];

其中,l和r分别代表元素的左右孩子,siz代表它的子树大小(根节点也算),cnt代表重复次数,val代表节点的值
下面,我们介绍一下二叉查找树的常见操作:
1.建树&&插入元素
建树先放一边 在插入元素的时候,通过与各个子树的根节点逐步比较,找到这个元素在二叉查找树上应该待的位置
建树操作:

if(sum==0){//如果还没有建树
    tree[++sum].val=d;
    tree[sum].cnt=1;
    tree[sum].siz=1;//就让tree[1]成为根节点
}
else{//如果已经建树
    add(1,d);//就从根节点开始比较
}

然后是插入操作:

void add(int p,int x){//这里的siz是泛指 实际操作的时候调了非常久就是因为对每个插入siz没有++
    if(tree[p].val==x){
        tree[p].siz++;//因为在后文的操作中 siz会代表其子树大小等等 所以一定要加
        tree[p].cnt++;
        return ;
    }//如果跟这个节点相等就直接cnt++然后返回
    if(tree[p].val<x){//如果大于根节点
        if(tree[p].r==0){//如果右子树刚好不存在 就进右子树
            tree[++sum].val=x;
            tree[p].siz++;
            tree[p].r=sum;
            tree[sum].siz=1;
            tree[sum].cnt=1;//初始化cnt和siz
        }
        else{
            tree[p].siz++;//这里也不能忘记加
            add(tree[p].r,x);//递归求解
        }
    }
    else{//当插入值小于根节点
        if(tree[p].l==0){//如果左子树不存在 就进左子树
            tree[++sum].val=x;
            tree[p].siz++;
            tree[p].l=sum;
            tree[sum].siz=1;
            tree[sum].cnt=1;
        }
        else{//同上
            tree[p].siz++;
            add(tree[p].l,x);//递归求解
        }
    }
}

2.在二叉查找树中查询某个数x的排名:
我们仍然从根节点开始查找,如果x大于根节点的话就对右子树查找,如果小于就对左子树查找

int querank(int p,int x){
    if(p==0) return 0;//这个要写
    if(tree[p].val==x){
        return tree[tree[p].l].siz;
    }//如果等于x就返回左子树大小
    if(tree[p].val<x){
        return tree[tree[p].l].siz+tree[p].cnt+querank(tree[p].r,x);
    }//如果大于就返回根节点个数加上左子树大小 再对右子树递归查找
    else{
        return querank(tree[p].l,x);//如果小于就对左子树查找
    }
}

3.在二叉查找树中查询排名为x的数:
仍然从根节点开始查找,如果根节点的左子树大小小于x就向右查找,反之向左查找

int quenum(int p,int x){
    if(p==0) return INF;//这个要写
    if(tree[tree[p].l].siz>=x){
        return quenum(tree[p].l,x);
    }//如果左子树比x大的话就向左查找
    if(tree[tree[p].l].siz+tree[p].cnt>=x){
        return tree[p].val;
    }//跟上面联动 如果在根节点处夹逼的话就返回根节点值
    return quenum(tree[p].r,x-tree[p].cnt-tree[tree[p].l].siz);//扣掉根节点和左子树,递归查找
}

4.在二叉查找树中查询x的前驱(小于x的最大值)
仍然从根节点开始查找,先判断x的大小跟根节点的差距,然后再向左子树/右子树递归查找

int quefront(int p,int x,int ans){
    if(tree[p].val>=x){//如果根节点不满足小于x
        if(tree[p].l==0){//如果左子树不存在 就返回ans
            return ans;
        }
        else{//否则对左子树查找
            return quefront(tree[p].l,x,ans);
        }
    }
    else{//如果根节点满足小于x
        if(tree[p].r==0){
            return tree[p].val;
        }//如果右子树不存在 那这就是最大值
        else{
            return quefront(tree[p].r,x,tree[p].val);//对右子树查找,同时更新ans
        }
    }
}

5.在二叉查找树中查询x的后继(大于x的最小值)
仍然从根节点开始查找,先判断x的大小跟根节点的差距,然后再向左子树/右子树查找

int queback(int p,int x,int ans){
    if(tree[p].val<=x){//如果根节点不满足大于x
        if(tree[p].r==0){//如果右子树不存在就返回ans
            return ans;
        }
        else{//否则对右子树查找
            return queback(tree[p].r,x,ans);
        }
    }
    else{//如果根节点满足大于x
        if(tree[p].l==0){//如果左子树不存在那么根节点就是后继
            return tree[p].val;
        }
        else{//否则更新ans并对左子树查找
            return queback(tree[p].l,x,tree[p].val);
        }
    }
}

6.删除二叉查找树的某个元素
令siz和cnt减一即可 但是对查询后继和前驱时需要对cnt进行特判 这里不再赘述
下面奉上完整代码 这可是我写的最长的一篇专栏呢

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{
    int l,r,siz,cnt,val;
};
node tree[1000005];
int n,opt,d,sum;
const int INF=0x7fffffff;
void add(int p,int x){
    if(tree[p].val==x){
        tree[p].siz++;
        tree[p].cnt++;
        return ;
    }
    if(tree[p].val<x){
        if(tree[p].r==0){
            tree[++sum].val=x;
            tree[p].siz++;
            tree[p].r=sum;
            tree[sum].siz=1;
            tree[sum].cnt=1;
        }
        else{
            tree[p].siz++;
            add(tree[p].r,x);
        }
    }
    else{
        if(tree[p].l==0){
            tree[++sum].val=x;
            tree[p].siz++;
            tree[p].l=sum;
            tree[sum].siz=1;
            tree[sum].cnt=1;
        }
        else{
            tree[p].siz++;
            add(tree[p].l,x);
        }
    }
}
int querank(int p,int x){
    if(p==0) return 0;
    if(tree[p].val==x){
        return tree[tree[p].l].siz;
    }
    if(tree[p].val<x){
        return tree[tree[p].l].siz+tree[p].cnt+querank(tree[p].r,x);
    }
    else{
        return querank(tree[p].l,x);
    }
}
int quenum(int p,int x){
    if(p==0) return INF;
    if(tree[tree[p].l].siz>=x){
        return quenum(tree[p].l,x);
    }
    if(tree[tree[p].l].siz+tree[p].cnt>=x){
        return tree[p].val;
    }
    return quenum(tree[p].r,x-tree[p].cnt-tree[tree[p].l].siz);
}
int quefront(int p,int x,int ans){
    if(tree[p].val>=x){
        if(tree[p].l==0){
            return ans;
        }
        else{
            return quefront(tree[p].l,x,ans);
        }
    }
    else{
        if(tree[p].r==0){
            return tree[p].val;
        }
        else{
            return quefront(tree[p].r,x,tree[p].val);
        }
    }
}
int queback(int p,int x,int ans){
    if(tree[p].val<=x){
        if(tree[p].r==0){
            return ans;
        }
        else{
            return queback(tree[p].r,x,ans);
        }
    }
    else{
        if(tree[p].l==0){
            return tree[p].val;
        }
        else{
            return queback(tree[p].l,x,tree[p].val);
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&opt,&d);
        if(opt==1) cout<<querank(1,d)+1<<endl;
        else if(opt==2) cout<<quenum(1,d)<<endl;
        else if(opt==3) cout<<quefront(1,d,-INF)<<endl;
        else if(opt==4) cout<<queback(1,d,INF)<<endl;
        else{
            if(sum==0){
                tree[++sum].val=d;
                tree[sum].cnt=1;
                tree[sum].siz=1;
            }
            else{
                add(1,d);
            }
        }
    }
    system("pause");
    return 0;
}
posted @ 2025-02-10 19:57  elainafan  阅读(130)  评论(0)    收藏  举报