【树状数组】LeetCode 307. Range Sum Query - Mutable

问题的提出

有一个数组 nums[0 . . . N-1],我们希望能提供如下两个功能:

  • 功能1:求出前 i 个元素的和 sum
  • 功能2:改变某个元素的值,即nums[i] = x

请参考 LeetCode 307. Range Sum Query - Mutable

一般情况下,功能1的时间复杂度为O(N)功能2的时间复杂度为O(1)
如果要改进功能1时间复杂度,我们可以依赖一个前缀和数组sum,用于记录前 i 个元素的和,即 sum[i] = nums[0] + nums[1] + ... + nums[i]。但此时,功能2的时间复杂度变为了O(N),因为需要去维护数组 sum

树状数组就是用来平衡这两个时间复杂度的,可以使得功能1功能2的时间复杂度都为O(logN)

引子 —— 一个位运算

获取数字 X 的最低比特位的计算式f(X)= X & -X
这个位运算举几个例子就大致明白了,具体证明也很简单,因为负数补码是正数按位取反再加1。实在不明白记住就行了,在此不再偏题赘述。

树状数组方法

创建数组BITree(图片中对应为数组C)

对于数组 a,我们设一个数组 BITree,有 BITree[i]=a[i-f(i)+1]+a[i-f(i)+2]+...+a[i]
其中f(i)就是取最低比特位的函数。
这里的数组aBITree都是从1开始算的,把索引0位置置零即可!
下图的C即为数组BITreelowbit(i)即为f(i)
在这里插入图片描述

计算i到j的和

计算方法:

sum(i,j)=sum(j)-sum(i-1)

代码如下:

    public int sumRange(int i, int j) {
        i++;
        j++;
        return sum(j) - sum(i - 1);
    }

上述的i++j++是索引0到索引1的转化过程。

接下来就要提供一个计算前缀和sum(index)的方法。
具体算法是

    public int sum(int index) {
        int res = 0;
        while (index > 0) {
            res += BITree[index];
            index = index - (index & -index);
        }
        return res;
    }


s u m ( k ) = B I T r e e [ n 1 ] + ⋯ + B I T r e e [ n m ] sum(k)=BITree[n_1]+\cdots+BITree[n_m] sum(k)=BITree[n1]++BITree[nm]
其中
n m = k , n i − 1 = n i − f ( n i ) , n 1 = f ( n 1 ) n_m=k, n_{i-1}=n_i-f(n_i), n_1=f(n_1) nm=k,ni1=nif(ni),n1=f(n1)
例如
s u m ( 6 ) = B I T r e e [ 6 ] + B I T r e e [ 4 ] sum(6)=BITree[6]+BITree[4] sum(6)=BITree[6]+BITree[4]

sum的构成证明

按照BITree的定义,展开每一项即可
在这里插入图片描述

求和复杂度证明

为了证明sum(i,j)复杂度是O(logN),只需要证明sum(N)复杂度为O(logN),证明思路为每次N都去掉了最低的二进制位,故最多循环O(logN)次。

更新算法

先更新a[i],再调用updateBIT方法更新BITree数组

    public void update(int i, int val) {
        i++;
        int diff = val - a[i];
        a[i]=val;
        updateBIT(i, diff);
    }

这里又涉及到判断BITree中哪些项需要更新。
具体代码如下:

    private void updateBIT(int i, int diff){
        while (i < arr.length) {
            BITree[i] += diff;
            i = i + (i & (-i));
        }
    }

更新规则如下:
在这里插入图片描述

更新过程的证明

采用数学归纳法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一个例子

LeetCode 307. Range Sum Query - Mutable 的代码解答

class NumArray {
    private int[] a;
    private int[] BITree;

    public NumArray(int[] nums) {
        a = new int[nums.length + 1];
        System.arraycopy(nums, 0, a, 1, nums.length);

        BITree = new int[nums.length + 1];
        // 初始化BITree数组
        int[] sum = new int[nums.length + 1];
        for (int i = 1; i < sum.length; i++) {
            updateBIT(i, a[i]);
        }
    }

    public void update(int i, int val) {
        i++;
        int diff = val - a[i];
        a[i]=val;
        updateBIT(i, diff);
    }

    private void updateBIT(int i, int diff){
        while (i < a.length) {
            BITree[i] += diff;
            i = i + (i & (-i));
        }
    }

    public int sumRange(int i, int j) {
        i++;
        j++;
        return sum(j) - sum(i - 1);
    }

    private int sum(int index) {
        int res = 0;
        while (index > 0) {
            res += BITree[index];
            index = index - (index & -index);
        }
        return res;
    }

参考链接

【1】https://www.jianshu.com/p/455fd78637be
【2】北大 acm 暑期课

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值