线段树

本文深入讲解了线段树这一高效的数据结构,介绍了其原理、构造方法及应用,包括线段树的修改和查询操作,展示了如何通过线段树实现快速查找和更新区间信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。 [1] 

对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。

线段树是建立在线段的基础上,每个结点都代表了一条线段[a,b]。长度为1的线段称为元线段。非元线段都有两个子结点,左结点代表的线段为[a,(a + b) / 2],右结点代表的线段为[((a + b) / 2)+1,b]。

下图就是两棵长度范围为[1,5][1,10]的线段树。

长度范围为[1,L] 的一棵线段树的深度为log (L) + 1。这个显然,而且存储一棵线段树的空间复杂度为O(L)。

线段树支持最基本的操作为插入和删除一条线段。下面以插入为例,详细叙述,删除类似。

将一条线段[a,b] 插入到代表线段[l,r]的结点p中,如果p不是元线段,那么令mid=(l+r)/2。如果b<mid,那么将线段[a,b] 也插入到p的左儿子结点中,如果a>mid,那么将线段[a,b] 也插入到p的右儿子结点中。

插入(删除)操作的时间复杂度为O(logn)。

线段树的修改

//线段树的修改
//方法一 
void modify(int p,int l,int r,int x,int v){
	s[p]+=v;
	if(l==r) return;//叶结点则退出
	int mid=(l+r)/2;
	if(x<=mid){//判断x在左儿子还是右儿子 
		modify(p*2,l,mid,x,v);
	}else{
		modify(p*2+1,mid+1,r,x,v);
	}
} 
//方法二
void up(int p){//将儿子结点的信息更新到父亲节点 
	s[p]=s[p*2]+s[p*2+1];
} 
void modify(int p,int l,int r,int x,int v){
	if(l==r){
		s[p]+=v;
		return;
	}
	int mid=(l+r)/2;
	if(x<mid){
		modify(p*2,l,mid,x,v);
	}else{
		modify(p*2+1,mid+1,r,x,v);
	}
	up(p);
}

线段树的查询

//线段树的查询
int query(int p,int l,int r,int x,int y){
	if(x<=l&&r<=y) return s[p];//若该结点被查询区间包含
	int mid=(l+r)/2,res=0;
	if(x<=mid) res+=query(p*2,l,mid,x,y);
	if(y>mid) res+=query(p*2+1,mid+1,r,x,y);
	return res;
} 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值