给你一个数组 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])
示例 1:
输入:
[“NumArray”, “sumRange”, “update”, “sumRange”]
[[[1, 3, 5]], [0, 2], [1, 2], [0, 2]]
输出:
[null, 9, null, 8]解释:
NumArray numArray = new NumArray([1, 3, 5]);
numArray.sumRange(0, 2); // 返回 1 + 3 + 5 = 9
numArray.update(1, 2); // nums = [1,2,5]
numArray.sumRange(0, 2); // 返回 1 + 2 + 5 = 8
提示:
这道题要直接遍历,肯定超时,不用想我帮你们试过了。
然后我就想到了前缀和的方式,将sunRange
转化为*O(1)*的方法,直接用sums[right] - sums[left-1]
去求和。
至于update
我选择使用一个change
存储变化量。在求和时,先遍历一遍「变化」计算出与最初数组的差值,然后再加上前缀和求出来的结果。我自以为这个方案是可行的。世事难料!又超时了!淦!
代码长这个样子:
var NumArray = function(nums) {
this._arr = nums // 正常数组
this.arr = new Array(nums.length) // 前缀和数组
this.arr[0] = nums[0]
for(let i = 0; i< this.arr.length; i++){
this.arr[i] = this.arr[i-1] + nums[i]
}
this.change = new Map()
};
NumArray.prototype.update = function(index, val) {
let change = val - this._arr[index]
this._arr[index] = val
this.change.set(index,(this.change.get(index) || 0) + change)
};
NumArray.prototype.sumRange = function(left, right) {
let sum = 0
// 先求出到目前为止的变化量
for(let [index,change] of this.change.entries()){
if(index >= left && index <= right) sum += change
}
if(left == 0 ) sum = sum + this.arr[right]
else sum = sum + this.arr[right] - this.arr[left-1]
return sum
};
之后就去参考了下别人的题解,见识了从没听过的数据结构和方法「树状数组」。这里是一篇讲解详细的题解。
其实大致的思想和上述差不多,也是利用前缀和,不过使用的结构是「树状数组」而上文使用的是普通线性数组,在查找效率以及更新效率上是不如树状的。
总而言之,我认为是更换了一种遍历方式,从「1、2、3……」到「1、2、1、4、1、6……」;当然看不懂这个遍历方式很正常,因为是树状的,通过x & -x
得出的;具体理解可见上边的题解,还有图解,我就不献丑了。
上代码:
// 不一样的遍历方式,「树状数组」
function lowbit(x){
return x & (-x)
}
// 更新前缀和数组(也可以初始化)
function insert (index, val, arr){
let x = index + 1
while(x < arr.length){
arr[x] = arr[x] + val
x += lowbit(x)
}
return arr
}
// 查询当前索引的前缀和
function query(x,sums){
let s = 0
while(x != 0) {
s += sums[x]
x -= lowbit(x)
}
return s
}
var NumArray = function(nums) {
this._arr = nums // 正常数组
this.arr = new Array(nums.length + 1).fill(0) // 树状前缀和数组
for(let i = 0; i< this.arr.length; i++){
// 初始化前缀和数组,(因为写的独立方法,所以传参有点多)
this.arr = insert(i, this._arr[i], this.arr)
}
};
/**
* @param {number} index
* @param {number} val
* @return {void}
*/
NumArray.prototype.update = function(index, val) {
let x = index + 1
while ( x < this.arr.length){ // 将更新应用到与之相关的数组项
this.arr[x] = this.arr[x] - this._arr[index] + val
x += lowbit(x)
}
this._arr[index] = val // 更新正常数组的值
};
/**
* @param {number} left
* @param {number} right
* @return {number}
*/
NumArray.prototype.sumRange = function(left, right) {
return query(right + 1,this.arr) - query(left,this.arr)
};
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/range-sum-query-mutable
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。