LeetCode307 区域和检索 - 数组可修改

题目介绍

给你一个数组 nums ,请你完成两类查询。

其中一类查询要求 更新 数组 nums 下标对应的值
另一类查询要求返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 ,其中 left <= right
实现 NumArray 类:

 - NumArray(int[] nums) 用整数数组 nums 初始化对象
 - void update(int index, int val) 将 nums[index] 的值 更新 为 val
 - int sumRange(int left, int right) 返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 (即,nums[left] + nums[left + 1], ..., nums[right]

最开始看题使用的暴力法,在操作次数和调用查询操作同阶市,时间复杂度 O ( n 2 ) O(n^2) O(n2),因此会超出时间限制,在看题解后知道了树状数组。思路如下:

在这里插入图片描述

将C[]数组的结点序号转化为二进制
1=(001) C[1]=A[1];
2=(010) C[2]=A[1]+A[2];
3=(011) C[3]=A[3];
4=(100) C[4]=A[1]+A[2]+A[3]+A[4];
5=(101) C[5]=A[5];
6=(110) C[6]=A[5]+A[6];
7=(111) C[7]=A[7];
8=(1000) C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];

第i个位置上的值为叶节点的和,这个和是通过二进制最后的0的个数所确定,一共需要2^k,k为0的个数,比如C[4] = A[0] + A[1] + A[2] + A[3],这是因为4的二进制数为:100,末尾一共有两个0,因此一共由前面4个数确定。

区间查询

下面利用C数组计算子缀和,当计算前i个子缀和时,假设i=7
sum[7] = A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]
前i项和 :sum[7] = C[4] + C[6] + C[7]
利用C数组是怎么算的呢?先看代码

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

这里先引进 l o w b i t lowbit lowbit函数: l o w b i t = x & ( − x ) lowbit = x \& (-x) lowbit=x&(x)

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

通过lowbit操作不断将目标点的最后一个1反转成0,达到前缀求和操作。比如
x = 7 = 111 x = 7 = 111 x=7=111
ans += C[7]
-x = 001
x & -x = 001
x - lowbit(x) = 110 = 6
ans += C[6]
-x = 010 = 2
x & -x = 010 = 2
x - lowbit(x) = 100 = 4
ans += C[4]
-x = 001
x & (-x) = 000 = 0
结束
可以看见C[7]通过3个值来计算出结果。上面说的“不断将目标点的最后一个1反转成0”操作,从计算过程中可以看出 111 --> 110 --> 100 --> 000

单点更新

当我们修改A[]数组中的某一个值时 应当如何更新C[]数组呢?
回想一下 区间查询的过程,再看一下上文中列出的图

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

可以发现 更新过程是查询过程的逆过程。由叶子结点向上更新C[]数组,也就是不断把1反转成0的过程。
当更新A[1]时 需要向上更新C[1] ,C[2],C[4],C[8]
C[1], C[2], C[4], C[8]
写为二进制 C[(001)],C[(010)],C[(100)],C[(1000)]
1(001) C[1]+=A[1]
lowbit(1)=001 1+lowbit(1)=2(010) C[2]+=A[1]
lowbit(2)=010 2+lowbit(2)=4(100) C[4]+=A[1]
lowbit(4)=100 4+lowbit(4)=8(1000) C[8]+=A[1]

那么初始化C数组就是不断读取A中的初始值,然后循环add函数

add(i+1, A[i]);

Reference

https://leetcode-cn.com/circle/article/F8YCEM/

https://www.cnblogs.com/hsd-/p/6139376.html

https://flushhip.blog.csdn.net/article/details/79165701?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_aa&utm_relevant_index=2

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值