线段树(单点修改,区间查询)

本文介绍了线段树的概念及其在解决区间修改和查询问题中的应用。通过一个具体的例题,展示了线段树如何高效处理大规模序列上的操作。线段树的每个节点代表一个区间的最大值,通过递归建树、修改和查询操作,实现了O(m log2 n)的时间复杂度。此外,线段树还可用于查询区间和、区间最小值等更多功能。
摘要由CSDN通过智能技术生成

线段树是一种二叉搜索树,用来区间修改维护以及查询。下面给出一道例题以便大家更好地理解线段树。

题目描述

在N(1<=N<=100000)个数A1…An组成的序列上进行M(1<=M<=100000)次操作,操作有两种:
(1)1 x y:表示修改A[x]为y;
(1)2 x y:询问x到y之间的最大值。

输入

第一行输入N(1<=N<=100000),表示序列的长度,接下来N行输入原始序列;接下来一行输入M(1<=M<=100000)表示操作的次数,接下来M行,每行为1 x y或2 x y。

输出

对于每个操作(2)输出对应的答案。

如果用暴力求解,肯定会爆,这时候我们就需要线段树了。

顾名思义,我们可以把线段树理解为一棵树。

在这里插入图片描述
每一个节点代表一个区间的最大值,黑色数字是每个节点所代表的区间的左右端点。红色数字是节点的编号。从图中,我们可以清晰地看出,如果一个非叶节点的编号为x,则它的左右子树的编号分别为2x和2x+1,且它的左右子树各占它的一半(有时候左右子树长度可能会相差1,不过不会有什么影响),每一个叶节点所代表的区间长度都为一。如果根节点的区间长度为n,则每一棵线段树的结点数都不超过2n-1。

我们可以用b数组来存储区间最大值。如果b[1]为根节点,则b[o]=max(b[o×2],b[o×2+1])。(想一想,为什么)

建树

我们可以递归调用bt函数,设置三个传参,分别为结点编号和结点所代表的区间的左右端点(设左端点为l,右端点为r,结点编号为o)。当l=r时,则该节点为叶节点,可以直接赋值,b[o]=a[l]。否则该节点为非叶节点,需递归调用,求出子节点的最大值后再赋值,b[o]=max(b[o×2],b[o×2+1])。建树代码:

void bt(int l,int r,int o){
   
	if(l==r) b[o]=a[l];//赋值
	else{
   
		int mid=(l+r)/2;
		bt(l,mid,o*2);
		bt(mid+1,r,o*2+1);
		b[o]=max(b[o*2],b[o*2+1]);
		//该节点为左右子树的最大值
	}
}

修改
在这里插入图片描述
上图,当a[3]被修改的时候,所有黄色的结点都有可能受到影响(即b[1] b[2] b[5] b[10])。这也提示我们,如果我们要把a[x]修改为y,所有包含x的结点都有可能受到影响,所以,在修改的时候,我们只需要递归调用包含y的结点进行修改。当递归到叶节点时,我们就可以直接修改。由于子节点被修改了,父节点也会受到影响,所以在修改完子节点后,我们还要重新给父节点赋值。修改代码:

void change(int l,int r,int o
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值