树套树学习笔记

算法简介

树套树,一种强大毒瘤的数据结构。因为嵌套的缘故,导致代码又难写,又难调。不过随之而来的,是其强大的能力。树套树的种类非常多,很多的树形结构都能互相嵌套。本文介绍其中最常见的一种,线段树套平衡树。(本文以树套树的板题P3380 【模板】二逼平衡树为基准)

思路与实现

空间复杂度

每种树套树因为所套的树不同,时间与空间上通常有很大的差异,一般会相差一两个 l o g log log ,而线段树套平衡树,很显然,因为线段树一共有 l o g n log n logn 层,每一层的节点个数都是 n 。所以一共有 n l o g n nlogn nlogn 个节点,而平衡树是对每个节点建立话费一点空间,所以总的空间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)

实现(+时间复杂度分析)

而时间复杂度的话,不同操作差异很大,我们分到每个操作里去分析。

修改位置 k 上的值

因为我们需要修改所有包含这个节点的平衡树,所以一共会修改这条链上的所有点。一共 l o g n logn logn 次。而每次平衡树修改的时间复杂度也是 l o g n logn logn 。所以总的时间复杂度为 l o g 2 n log^2n log2n

void change(int rt,int l,int r,int x,int k){
   
	int mid=(l+r)>>1;
	FT[rt].outint(a[x]);//删除 x位置上的数
	FT[rt].init(k);//插入k
	if(l==r)return ;//到叶子节点了,结束 
	if(x<=mid)change(rt<<1,l,mid,x,k);//在左子树 
	else change(rt<<1|1,mid+1,r,x,k);//要修改一条链上的所有点
	return ; 
}

查询 k 在区间内的排名

因为是查询区间排名。那我们肯定要先在线段树中查询到这个区间。然后每个区间查询比k小的数有多少,把值加起来就可以了。总的复杂度就是 O ( l o g 2 n ) O(log^2n) O(log2n)

int find_rank(int rt,int l,int r,int L,int R,int k){
   //查询排名
	int ans=0;
	if(L<=l&&r<=R){
   //在查询区间
		ans=FT[rt].findrank(k);
		return ans;//有几个比它小 
	}
	int mid=(l+r)>>1;
	if(L<=mid)ans+=find_rank(rt<<1,l,mid,L,R,k);
	if(mid+1<=R)ans+=find_rank(rt<<1|1,mid+1,r,L,R,k);
	return ans;
}

查询区间排名为 k 的数

显示跟上一问一样,我们还是要先找到这个区间,但是我们发现区间排名为 k 的数这个问题并不能像上一问一样合并两个区间的答案。所以我们只能在最前面在加上一个二分。因为区间排名为 k 的数显然有单调性。值越大,显然排名越大。因为加了一个二分,所以时间复杂度变为了 O ( l o g 3 n ) O(log^3n) O(log3n)

int find_k(int l,int r,int rank){
   //找排名为k的数 
	int a=0,b=1e8,mid,sum,ans=-1;
	mid=(a+b)>>1;
	while(a<=b){
   //使用二分查找,因为数越大,排名越高 
		mid=(a+b)>>1;
		sum=find_rank(1,1,n,l,r,mid);
		if(sum+1<=rank){
   
			ans=mid;
			a=mid+1;//排名小了
		
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值