学习笔记1 二叉搜索树[Binary Search Tree]
time stamp:2019/11/18
前置知识:二叉树
准备工作
对于一棵二叉搜索树来说,设它的任意一个节点的权值为V,设其左节点的权值为V1,设其右节点的权值为V2,则必定满足如下规律:
V1 < V 或 V1 = 0(空节点);
V2 > V 或 V2 = 0(空节点).
那么有了这样的性质,我们就可以利用他来维护一个不重复的有序序列(当然可以计次数),也可以用它来查询有序区间的值,平均复杂度为O(nlog2n).但是当这种数据结构遇到有序的数据(如99999,99998,99997…)是会直接被卡成一条链(最坏情况),此时的时间复杂度为O(n2).这个问题可以用平衡树来解决,但是由于我太懒了所以我们先来看二叉搜索树怎么写.
变量声明:
t[i].siz表示以 i 这个节点为根的子树中的元素个数,t[i].l,t[i].r表示i的左右子树,t[i].num表示这个节点的计数次数,t[i].val表示当前节点的权值,siz表示整棵树的大小.
讲解都放在注释里了,希望你能看得开心看懂~
建树
先上代码(我的蒟蒻代码)
struct bintree{
int val;
int l,r;
int num;
int siz;
}t[1000000];
维护操作
直接上代码吧,我相信是个人都能看懂
void update(int x)
{
t[x].siz=t[t[x].l].siz+t[t[x].r].siz+1;//因为自身也需要计入所以+1
}
插入操作
还是先上代码
siz毒瘤写法
bool inst(int x,int c)//当前要插入的值和当前的根节点
{
if(!c)//如果搜索到一个空节点就返回真,表明这个节点需要被安排(
{
t[++siz].val=x;//整棵树大小自加,输入权值
t[siz].num++;
t[siz].siz++;
return true;
}
if(x==t[c].val)//当前节点在这棵树中已存在
{
t[c].num++;
return false;
}
if(t[c].val>x)//当前搜到的节点权值比它大
{
if(inst(x,t[c].l))//去左子树
{
t[c].l=siz;//需要安排就安排一下他,树的大小同时也是最新插入结点的编号
update(c);//更新一下当前结点的值
}
return false;
}
if(t[c].val<x)//当前搜到的节点权值比它小
{
if(inst(x,t[c].r))//去右子树
{
t[c].r=siz; //安排他
update(c);
}
return false;
}
}
查询操作
还是先上代码
int query(int x,int c,bool type)//这个东西的正确读法不是 缺瑞 ,是 Q瑞!!!
{
if(!t[c].val)return -1;//空子树,表明当前查询的值不存在
if(t[c].val==x)return type?t[c].siz:t[c].num;//type为真查询子树大小,为假则查询元素出现的次数
if(t[c].val<x)//一样的道理
{
return query(x,t[c].r,type);
}
if(t[c].val>x)
{
return query(x,t[c].l,type);
}
}
完整代码
我们遇到什么困难,也不要怕,微笑着去面对它…(被打死
struct bintree{
int val;
int l,r;
int num;
int siz;
}t[1000000];
int N,M;//数据数量
int siz=1;//一棵树当然要有一个根
bool inst(int x,int c)//当前要插入的值和当前的根节点
{
if(!c)//如果搜索到一个空节点就返回真,表明这个节点需要被安排(
{
t[++siz].val=x;//整棵树大小自加,输入权值
t[siz].num++;
t[siz].siz++;
return true;
}
if(x==t[c].val)//当前节点在这棵树中已存在
{
t[c].num++;
return false;
}
if(t[c].val>x)//当前搜到的节点权值比它大
{
if(inst(x,t[c].l))//去左子树
{
t[c].l=siz;//需要安排就安排一下他,树的大小同时也是最新插入结点的编号
update(c);//更新一下当前结点的值
}
return false;
}
if(t[c].val<x)//当前搜到的节点权值比它小
{
if(inst(x,t[c].r))//去右子树
{
t[c].r=siz; //安排他
update(c);
}
return false;
}
}
int query(int x,int c,bool type)//这个东西的正确读法不是 缺瑞 ,是 Q瑞!!!
{
if(!t[c].val)return -1;//空子树,表明当前查询的值不存在
if(t[c].val==x)return type?t[c].siz:t[c].num;//type为真查询子树大小,为假则查询元素出现的次数
if(t[c].val<x)//一样的道理
{
return query(x,t[c].r,type);
}
if(t[c].val>x)
{
return query(x,t[c].l,type);
}
}
int query(int x,int c,bool type)//这个东西的正确读法不是 缺瑞 ,是 Q瑞!!!
{
if(!t[c].val)return -1;//空子树,表明当前查询的值不存在
if(t[c].val==x)return type?t[c].siz:t[c].num;//type为真查询子树大小,为假则查询元素出现的次数
if(t[c].val<x)//一样的道理
{
return query(x,t[c].r,type);
}
if(t[c].val>x)
{
return query(x,t[c].l,type);
}
}
int main()
{
ios::sync_with_stdio(false);
//这里介绍一个小技巧:上面这句关闭流同步后cin会变得几乎和scanf一样快,但是关闭之后C标准输入输出[cstdio](getchar,putchar,scanf,printf...都不能用了)
cin>>N;
int x;
for(int i=1;i<=N;i++)
{
cin>>x;
insert(x,1);//从根节点开始访问
}
cin>>M;
int type;
for(int i=1;i<=M;i++)
{
cin>>x>>type;
cout<<query(x,1,type)<<endl;
}
return 114514;
}
好的我写完了