【洛谷P5076】普通二叉树(简化)
今天接着学二叉树
说实在话当我看到这题的时候
诶?这给我干哪儿来了 这还是国内吗
然后就翻书学了一种新的数据结构 二叉查找树(又称二叉搜索树)
当然这个用理解记忆就可以了 后面会用平衡树进行完善
因为二叉查找树在退化成链的时候可能会变成O(\(n^2\))
我们先看一下题目
【深基16.例7】普通二叉树(简化版)
题目描述
您需要写一种数据结构,来维护一些数(都是绝对值 \(10^9\) 以内的数)的集合,最开始时集合是空的。其中需要提供以下操作,操作次数 \(q\) 不超过 \(10^4\):
- 定义数 \(x\) 的排名为集合中小于 \(x\) 的数的个数 \(+1\)。查询数 \(x\) 的排名。注意 \(x\) 不一定在集合里。
- 查询排名为 \(x(x\ge 1)\) 的数。保证集合里至少有 \(x\) 个数。
- 求 \(x\) 的前驱(前驱定义为小于 \(x\),且最大的数)。若不存在则输出 \(-2147483647\)。
- 求 \(x\) 的后继(后继定义为大于 \(x\),且最小的数)。若不存在则输出 \(2147483647\)。
- 插入一个数 \(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;
}

浙公网安备 33010602011771号