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

LeetCode307 区域和检索 - 数组可修改,第1张

题目介绍
给你一个数组 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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/565126.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-06
下一篇 2022-04-06

发表评论

登录后才能评论

评论列表(0条)

保存