线段树--用于海量数组中高效查询与修改1

经典场景:

一个给定较大的数组X:

要求1: 可以随时更新某个区间[i,j]内的元素

要求2:可以查询火统计某个区间[i,j]内最值/和等。

常见做法:

对于要求1,遍历区间[i, j],进行元素的更新,时间复杂度 O ( n ) ;

对于要求2,遍历区间[i, j],查询最值,时间复杂度 O ( n ) ;

如数组元素个数较大时,耗时严重。由此引入线段树,保证对数数据内更新火查询某个区间。

线段树定义:

线段树是一种利用分治思想处理对一段序列进行大量区间操作(修改、查询)的数据结构。线段树的叶节点即为原序列,内部节点除了左右儿子之外,还保存着其分管的某段区间信息,如区间和、区间最值。显然,每个节点所分管的区间即为以其为根的子树中所有叶节点。

预处理生成线段树:耗时O ( n )

空间复杂度O ( nlogn )

区间更新时间复杂度O (log n )

区间查询时间复杂度O (log n )

假设数组 x 为:x = [ 2, 5, 1, 4, 9, 3 ] ,一共 n=6 个元素,则构建的线段数如下

 

意构建的时候,在非叶子节点更新你想要存储的区间信息(区间最大值,区间最小值等,这里选择最小值)

区间查询

查询[1,4]内的最小值

1 找到区间范围内的最下曾节点(不一定是叶子)

图中标记 * 号的节点为当前需要查询的节点,注意区间[3-4]不需要查询叶子,本身已经是子区间范围内最值。故[1, 4] 区间包含的节点是:1-1, 2-2, 3-4

2 递归向上查询最小值

对应的最小值分别x[1-1]=5,x[2,2]=1,x[3,4]=4

min(x14) = min(x12, x34)
         = min(min(x11, x22), 4)
         = min(min(5, 1), 4)
         = min(1, 4)
         = 1

区间更新

[1, 4] 之间所有元素加 3

1 找到需要更新的最下层节点(类似的,也不一定是叶子)

 

图中标记 * 号的节点为当前需要更新的节点,类似的,故[1, 4] 区间包含的节点是:1-1, 2-2, 3-4

2 递归向上更新区间

图中标记 * 或 # 号的节点为更新过程中需要更新最小值的节点。[3, 4] 完全处于 [1, 4] 的更新范围内,在更新时无需更新其所有子节点。但此时但是按照上图的结果,如果我进行查询 [3, 3] 的最小值(或者更新值)。显然结果与图示的数据不符合([3,3]最小值是4但是区间[3-4]显示最小值是7,矛盾!!!)。

所以需要增加更新标志位,处理上面所提及的情况

该标志位记录当前区间的变更值。方便后续在进行涉及该区间的子节点查询 / 更新时,需要取出该值递归更新沿途的区间。

如后续查询 [3, 3]时,

其中 * 号为更新后的值,# 号为传递的标志位。在更新了区间后,需要把原有的 [3, 4] 标志位清除(否则下次会导致重复更新)。

在此基础尚,需区间 [4, 5] 所有元素 减去2

把区间标志位和变更值一起传递(注意区间范围),同时清除已经更新完的区间标志位。

最终结果

代码实现:

线段树是一种二叉树,当然可以像一般的树那样写成结构体,也可以用数组来实现树形结构,可以大大简化代码。

假设某个节点的编号为v,那么它的左子节点编号为2*v,右子节点编号为2*v+1。

然后规定根节点为1.这样一颗二叉树就构造完成了。通常2*v在代码中写成 v<<1 。 2*v+1写成 v<<1|1

 

更详细参考这里

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值