学习记录#1 二叉搜索树

学习笔记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;	
}

好的我写完了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值