307. 区域和检索 - 数组可修改

分析

常规做法

brute force的做法是,更新时直接在数组原地更新,而求区域和时逐元素相加,时间复杂度为 O ( n ) O(n) O(n),较高。

线段树解法与其结构

使用线段树可以优化sumRange效率,根据官方题解

线段树 segmentTree 是一个二叉树,每个结点保存数组 nums 在区间 [s,e] 的最小值、最大值或者总和等信息。线段树可以用树也可以用数组(堆式存储)来实现。

比如下图,对于数组[1,3,5,7,9,11],根节点存储了数组[0,5]之间的区域和为36。

更具体地分析线段树的结构,如下图所示:

  • 令根节点存储[0,5]的和为36
  • 然后划分均等的两部分,前者存储[0,2]的区域和为9,后者存储[3,5]的区域和为27。
  • 这两部分再均等划分,直到区域范围为1为止。

线段树的构建

如何构建线段树?如下图右侧代码所示,用后序遍历的方式,先构建左右子树,再构建根节点。

线段树的区域查询

如何在线段树中查询区域和?根据条件判断在左右子树作递归处理。下图以查询[2,5]区间为例,进行了示范。该查询最终会被划分到对[2,2]和[3,5]的查询。

在这里插入图片描述

线段树的更新

然后是线段树的更新操作,单边递归即可。先更新其子节点的区域和,再更新根节点的区域和。

线段树的时间复杂度

线段树的范围查询时间复杂度为 O ( l o g n ) O(logn) O(logn),因为每层会访问的节点最多4个,而树高度为 O ( l o g n ) O(logn) O(logn),所以总访问次数为 4 ∗ O ( l o g n ) = O ( l o g n ) 4 * O(logn)=O(logn) 4O(logn)=O(logn)

证明范围查询的时间复杂度 看Ashwini Anand的回答。看图先获得直观的感受。

segment-tree-query-complexity 看Wu-Man

首先,第一层最多有两个子节点,所以到了第3层,最多有4个节点被访问。

现在假设在第i(i>=3)层访问了最多4个节点,我们要证明在第i+1层也访问最多4个节点。假如第i层只访问1或2个节点,那么第i+1层会访问最多4个节点,因为每个节点最多有两个子节点。

那么如果第i层访问了3或4个节点呢?我们假设第i层访问的节点有3个类型:

  1. 最右侧的单节点,节点范围靠左的部分被查询覆盖。
  2. 最左侧的单节点,节点范围靠右的部分被查询覆盖。
  3. 1或2个中间节点,其范围被查询完整覆盖。

由于中间节点是被查询完整覆盖的,其不会递归到下一层。而左右端的节点最多产生两个子节点。所以到下一层查询最多只有4个节点。

类型3的节点不会被递归到下一层,而类型1和2的节点至多各有一个,否则就破坏了连续范围的定义,比如有两个范围靠左部分覆盖的节点,它们中间的部分一定存在“空白“,显然不连续

第3层后是否有左右都是被查询部分覆盖的节点?假如有这样的节点,该层不会有其它类型的节点了,否则一定会查询不连续。
以此类推,到第i层最多都只有4个节点被访问。

我们没法再往下严谨地证明了,希望有深入研究的朋友能给出数学层面的证明

拓展阅读

线段树比较适合用于数组的范围查询,比如下面两题:

在过去的刷题经验中,涉及到数组区域的题目,可能用到滑动窗口或者前缀数组,但当涉及更新操作时,线段树的对数时间复杂度能使其成为重要解题路径。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值