二叉树 之 二叉搜索树 or 二叉排序树

二叉树还是一脸懵逼…今天对着题解写了一个二叉搜索树的题…还是慢慢学吧
一会去续写最短路
题目地址
详细题解及二叉搜索树介绍
上面这个题解相当详细!!!强烈推荐!!
想了解二叉排序树就点上面的,我只做一些自己的代码整理笔记

我在原题解上面改了一部分代码,关于对着排名查找值的部分
原作者这样写的

int queryrk(int x,int rk)
{
    if(x==0) return INF; 
    if(tree[tree[x].ls].siz>=rk)//如果左子树大小>=rk了,就说明答案在左子树里 
        return queryrk(tree[x].ls,rk);//查左子树 
    if(tree[tree[x].ls].siz+tree[x].cnt>=rk)//如果左子树大小加上当前的数的多少恰好>=k,说明我们找到答案了 
        return tree[x].val;//直接返回权值 
    return queryrk(tree[x].rs,rk-tree[tree[x].ls].siz-tree[x].cnt);
    //否则就查右子树,同时减去当前节点的次数与左子树的大小 
}

我这样写的

int query_id(int pos,int rk)
{
    if(tree[pos].ls!=0&&tree[tree[pos].ls].sz>=rk)
    {
        return query_id(tree[pos].ls,rk);
    }
    else if(tree[pos].cnt+tree[tree[pos].ls].sz>=rk)
    {
        return tree[pos].val;
    }
    else 
    {
        return query_id(tree[pos].rs,rk-tree[pos].cnt-tree[tree[pos].ls].sz);
    }
}

虽然差不多,,但我自己改的我更好理解一点

--------------------------------------------
关于二叉排序树我们要记住一个特性
对于一个节点,它的左子树里面的值是全部小于它的,它的右子树里面的值是全部大于它的。。这个很重要
建树时
我们需要先人为增加第一个节点,之后的节点用add函数从第一个节点为起始点建立,比如

if(ope==5&&num==0)
{
     num++;
     tree[num].ls=tree[num].rs=0;
     tree[num].val=val;
     tree[num].cnt=tree[num].sz=1;
     continue;
}

在这个题目里,num为节点数目,ope为操作,当需要建立节点且当前节点数目是0时,我们就手动输入一个节点
之后我们就可以用函数add了,比如在树里面插入一个val值
我们先调用

add(1,val)

函数是

void add(int pos,int val)//当前pos位置  需要建立一个权值为val的子树
{
    //既然经过了这个点  那一定会存在这个点后面
    tree[pos].sz++;
    if(tree[pos].val==val)
    {
        //这个值已经出现过 直接计数就可以了
        tree[pos].cnt++;
        return ;
    }
    //左子树
    if(val<tree[pos].val)
    {
        //左子树不存在 建立一个子树
        if(tree[pos].ls==0)
        {
            num++;
            tree[num].val=val;
            tree[num].cnt=tree[num].sz=1;
            tree[num].ls=tree[num].rs=0;
            tree[pos].ls=num;
        }
        else///存在就继续递归
        {
            add(tree[pos].ls,val);
        }
    }
    //右子树  同上  反过来
    else 
    {
        if(tree[pos].rs==0)
        {
            num++;
            tree[num].val=val;
            tree[num].cnt=tree[num].sz=1;
            tree[num].ls=tree[num].rs=0;
            tree[pos].rs=num;
        }
        else 
        {
            add(tree[pos].rs,val);
        }
    }
}

具体步骤里面有详细注释,我们需要根据二叉树特性来判断val大小来不断递归就好了
查询前驱

int query_fr(int pos,int val,int ans)//求val的前驱 当前pos 当前已出现答案ans
{
    //当前数大了  需要向左树找
    if(tree[pos].val>=val)
    {
        //左子树不存在
        if(tree[pos].ls==0)
        {
            return ans;
        }
        else 
        {
            return query_fr(tree[pos].ls,val,ans);
        }
    }
    else 
    {
        //当前小于val 符合条件  看是否可以更加逼近
        //往大的方向走
        if(tree[pos].rs==0)
        {
            //没有更大的数了  那就是当前
            //因为我们的过程是后面的答案一定大于前面的
            return tree[pos].val;
        }
        else
        {
            //考虑到还有删除的可能  全面一点判断下数目
            //当前节点个数不为0我们就可以更新答案为当前权值
            if(tree[pos].cnt!=0)
            {
                ans=tree[pos].val;
            }
            return query_fr(tree[pos].rs,val,ans);
        }
        
    }
}

后驱同理

int query_ba(int pos,int val,int ans)//同上  反过来即可
{
    if(tree[pos].val<=val)
    {
        if(tree[pos].rs==0)
        {
            return ans;
        }
        else 
        {
            return query_ba(tree[pos].rs,val,ans);
        }
    }
    else 
    {
        if(tree[pos].ls==0)
        {
            return tree[pos].val;
        }
        else 
        {
            if(tree[pos].cnt!=0)
            {
                ans=tree[pos].val;
            }
            return query_ba(tree[pos].ls,val,ans);
        }
    }
}

查找排名

int query_rk(int pos,int val)//找出val值前面的数的个数
{
    if(pos==0){return 0;}
    if(tree[pos].val==val)
    {
        return tree[tree[pos].ls].sz;
    }
    else if(tree[pos].val<val)
    {
        return query_rk(tree[pos].rs,val)+tree[pos].cnt+tree[tree[pos].ls].sz;
    }
    else 
    {
        return query_rk(tree[pos].ls,val);
    }
}

从排名找值

int query_id(int pos,int rk)
{
    if(tree[pos].ls!=0&&tree[tree[pos].ls].sz>=rk)
    {
        return query_id(tree[pos].ls,rk);
    }
    else if(tree[pos].cnt+tree[tree[pos].ls].sz>=rk)
    {
        return tree[pos].val;
    }
    else 
    {
        return query_id(tree[pos].rs,rk-tree[pos].cnt-tree[tree[pos].ls].sz);
    }
}

调用时

switch(ope)
{
     case 1: ans=query_rk(1,val)+1;break;
     case 2: ans=query_id(1,val);break;
     case 3: ans=query_fr(1,val,-INF);break;
     case 4: ans=query_ba(1,val,INF);break;
     case 5: add(1,val);break;
}

找前驱先输入一个-INF来当最小值,后驱找INF来当最大值
AC代码

//#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<map>
#include<set>
#include<utility>
#define de(x)       printf("\n-------%d-------\n",x);
#define rep(i,a,n)  for(int i=a;i<=n;i++)
#define per(i,n,a)  for(int i=n;i>=a;i--)
#define ms(arr,x)   memset((arr),x,sizeof(arr))
#define IOS ios::sync_with_stdio(false),cin.tie(NULL)
#define ll long long
#define MP make_pair
#define pb push_back
#define inf 0x3f3f3f3f
#define INF 0x7fffffff
#define fi  first
#define se  second
/*
#define ls  (rt<<1)
#define rs  (rt<<1|1)
#define md  (l+r>>1)
*/
using namespace std;
typedef pair<int,int> pii;
inline int read()
{
    int ans=0,sign=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            sign=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        ans=ans*10+(ch-'0');
        ch=getchar();
    }
    return sign*ans;
}
const int mod=1e9+7;
const double PI=acos(-1.0);
const double eps=1e-5;
const int maxn=1e4+5;
const int maxm=1e5+5;
struct node
{
    int val,cnt,sz,ls,rs;
    //分别为
    //权值  该权值出现次数  
    //子树大小与自己大小之和
    //左子树  右子树
}tree[maxn];
int num;
void add(int pos,int val)//当前pos位置  需要建立一个权值为val的子树
{
    //既然经过了这个点  那一定会存在这个点后面
    tree[pos].sz++;
    if(tree[pos].val==val)
    {
        //这个值已经出现过 直接计数就可以了
        tree[pos].cnt++;
        return ;
    }
    //左子树
    if(val<tree[pos].val)
    {
        //左子树不存在 建立一个子树
        if(tree[pos].ls==0)
        {
            num++;
            tree[num].val=val;
            tree[num].cnt=tree[num].sz=1;
            tree[num].ls=tree[num].rs=0;
            tree[pos].ls=num;
        }
        else///存在就继续递归
        {
            add(tree[pos].ls,val);
        }
    }
    //右子树  同上  反过来
    else 
    {
        if(tree[pos].rs==0)
        {
            num++;
            tree[num].val=val;
            tree[num].cnt=tree[num].sz=1;
            tree[num].ls=tree[num].rs=0;
            tree[pos].rs=num;
        }
        else 
        {
            add(tree[pos].rs,val);
        }
    }
}
int query_fr(int pos,int val,int ans)//求val的前驱 当前pos 当前已出现答案ans
{
    //当前数大了  需要向左树找
    if(tree[pos].val>=val)
    {
        //左子树不存在
        if(tree[pos].ls==0)
        {
            return ans;
        }
        else 
        {
            return query_fr(tree[pos].ls,val,ans);
        }
    }
    else 
    {
        //当前小于val 符合条件  看是否可以更加逼近
        //往大的方向走
        if(tree[pos].rs==0)
        {
            //没有更大的数了  那就是当前
            //因为我们的过程是后面的答案一定大于前面的
            return tree[pos].val;
        }
        else
        {
            //考虑到还有删除的可能  全面一点判断下数目
            //当前节点个数不为0我们就可以更新答案为当前权值
            if(tree[pos].cnt!=0)
            {
                ans=tree[pos].val;
            }
            return query_fr(tree[pos].rs,val,ans);
        }
        
    }
}
int query_ba(int pos,int val,int ans)//同上  反过来即可
{
    if(tree[pos].val<=val)
    {
        if(tree[pos].rs==0)
        {
            return ans;
        }
        else 
        {
            return query_ba(tree[pos].rs,val,ans);
        }
    }
    else 
    {
        if(tree[pos].ls==0)
        {
            return tree[pos].val;
        }
        else 
        {
            if(tree[pos].cnt!=0)
            {
                ans=tree[pos].val;
            }
            return query_ba(tree[pos].ls,val,ans);
        }
    }
}
int query_rk(int pos,int val)//找出val值前面的数的个数
{
    if(pos==0){return 0;}
    if(tree[pos].val==val)
    {
        return tree[tree[pos].ls].sz;
    }
    else if(tree[pos].val<val)
    {
        return query_rk(tree[pos].rs,val)+tree[pos].cnt+tree[tree[pos].ls].sz;
    }
    else 
    {
        return query_rk(tree[pos].ls,val);
    }
}
int query_id(int pos,int rk)
{
    if(tree[pos].ls!=0&&tree[tree[pos].ls].sz>=rk)
    {
        return query_id(tree[pos].ls,rk);
    }
    else if(tree[pos].cnt+tree[tree[pos].ls].sz>=rk)
    {
        return tree[pos].val;
    }
    else 
    {
        return query_id(tree[pos].rs,rk-tree[pos].cnt-tree[tree[pos].ls].sz);
    }
}
void init()
{
    tree[0].ls=tree[0].rs=0;
    tree[0].sz=tree[0].cnt=0;
    tree[0].val=0;
}
void solve()
{
    init();
    num=0;
    int t=read();
    while(t--)
    {
        int ope=read(),val=read();
        int ans=0;
        if(ope==5&&num==0)
        {
            num++;
            tree[num].ls=tree[num].rs=0;
            tree[num].val=val;
            tree[num].cnt=tree[num].sz=1;
            continue;
        }
        switch(ope)
        {
            case 1: ans=query_rk(1,val)+1;break;
            case 2: ans=query_id(1,val);break;
            case 3: ans=query_fr(1,val,-INF);break;
            case 4: ans=query_ba(1,val,INF);break;
            case 5: add(1,val);break;
        }
        if(ope!=5)
        {
            printf("%d\n",ans);
        }
    }
}
int main()
{
    solve();
    #ifdef _LOCALE_DEBUG_PAUSE
    system("pause");
    #endif //_LOCALE_DEBUGE_PAUSE
    return 0;
}

猜你喜欢

转载自blog.csdn.net/leoxe/article/details/105328366