二叉树还是一脸懵逼…今天对着题解写了一个二叉搜索树的题…还是慢慢学吧
一会去续写最短路
题目地址
详细题解及二叉搜索树介绍
上面这个题解相当详细!!!强烈推荐!!
想了解二叉排序树就点上面的,我只做一些自己的代码整理笔记
我在原题解上面改了一部分代码,关于对着排名查找值的部分
原作者这样写的
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;
}