区间求和 之`树状数组` -leetcode-307. 区域和检索 - 数组可修改

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

在这里插入图片描述
在这里插入图片描述

引言

树状数组适用于什么场景?

  • 答:单点修改、区间查询

lowbit 运算

lowbit() 运算:非负整数 n n n在二进制表示下 最低位 1 及其后面的 0 构成的数值

  • 代码如下
    int lowbit(int x) {
    	return x & -x;
    }
    


树状数组概念

对于一个序列,我们在其上建立一个如下如所示的森林结构。

每个节点 t[x] 保存以 x x x (包括 x x x)为根的子树中叶节点值的和

那么每个节点覆盖的长度是多少呢?

  • 把每个节点t[x] x x x 转化为二进制后,我们会发现每一层的末尾的零的个数都是相同的;(如下图所示)
  • 而且零的个数与其覆盖的长度有关,即 t[x] 节点覆盖的长度就是 lowbit(x)

此外,由下图可知,t[x]节点的父节点为 t[x+lowbit(x)]


其中,
在这里插入图片描述

add

单点修改:add(int x, int val)

add(3, 5) 为例,

在整颗树上维护这个值,需要一层一层向上找到父节点,并按照需要修改这个节点的值,即 + 5 +5 +5

代码如下:

	void add(int x, int u) {
        for (int i = x; i <= n; i += lowbit(i)) tree[i] += u;
    }

一层都经过了处理即最坏复杂度为 O ( l o g n ) O(log n) O(logn)

query

前缀和查询:int query(int x),查询节点 x x x 的前缀和。

查询这个点 x x x的前缀和,需要从这个点向左上找到上一个节点,并加上其节点的值

可以发现向左上找上一个节点只需要将下标 -lowbit(x)

代码如下:

	int query(int x) {
        int ans = 0;
        for (int i = x; i > 0; i -= lowbit(i)) ans += tree[i];
        return ans;
    }

如果需要求出 区间和,我们可以分别求出区间左、右端点的前缀和并相减

在这里插入图片描述

树状数组

思路:

  • 本题便是“单点修改,区间求和”,直接套用如上 树状数组模板,即可

注意:树状数组是从 1 1 1 开始,而不是从 0 0 0 开始!!!

class NumArray {
    int[] tree;

    int lowbit(int x) {
        return x & -x;
    }

    int query(int x) {
        int res = 0;
        for (int i = x; i > 0; i -= lowbit(i)) {
            res += tree[i];
        }
        return res;
    }

    void add(int x, int val) {
        for (int i = x; i <= n; i += lowbit(i)) {
            tree[i] += val;
        }
    }

    private int[] nums;
    private int n;

    public NumArray(int[] nums) {
        this.nums = nums;
        this.n = nums.length;
        // 初始化树状数组(一定从1开始!)
        tree = new int[n + 1];
        for (int i = 0; i < n; i++) {
            add(i + 1, nums[i]);
        }
    }
    
    public void update(int index, int val) {
        // 向上更新父节点
        add(index + 1, val - nums[index]);
        nums[index] = val;
    }
    
    public int sumRange(int left, int right) {
        // 区间[left, right]元素之和,等于right+1前缀和 减去 left的前缀和
        return query(right + 1) - query(left);
    }
}

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray obj = new NumArray(nums);
 * obj.update(index,val);
 * int param_2 = obj.sumRange(left,right);
 */

在这里插入图片描述

参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值