您的位置:首页 > 产品设计 > UI/UE

Range Sum Query - Mutable:对区域内的值经常改变的数组求区域内的和 线段数

2017-11-06 10:25 323 查看
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.
The update(i,
val) function modifies nums by
updating the element at index i to val.

Example:

Given nums = [1, 3, 5]

sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8


Note:

The array is only modifiable by the update function.
You may assume the number of calls to update and sumRange function is distributed evenly.

思路一:因为区域内的值经常改变,朴素的直接查找范围需要O(n),会超时,因此考虑线段树
可以看看这个链接,关于线段树,写的挺好的。
https://leetcode.com/problems/range-sum-query-mutable/solution/
时空复杂度:

预处理:O(n) O(N)

查询:O(logN) O(1)

更新:O(logN) O(1)

class NumArray {
int[] segtree;
int n;
//int[] lazy;
public NumArray(int[] nums) {
n = nums.length;
segtree = new int[2*nums.length];
buildTree(nums);
}

public void buildTree(int[] nums){
//构建线段树,其父节点包含两个子节点区域的求和信息
//存储时segtree[]中 n到2n-1存储的叶子节点(nums数组),1-n-1存储的中间节点(区域信息),0位置未使用
for(int i = n,j = 0;i<2*n&&j<n;i++,j++){
segtree[i] = nums[j];
}
for(int i =n-1;i>0;i--){
segtree[i] = segtree[2*i] + segtree[2*i+1];
}
}

public void update(int i, int val) {
int pos = i+n;
segtree[pos] = val;
while(pos>0){
int left = pos;
int right = pos;
if(pos%2==0)//因为构建树时,i = i*2 + i*2+1,所以左孩子为2*n为偶数,右孩子为2*n+1为奇数
{
right = pos+1;
}else{
left = pos -1;
}
segtree[pos/2] = segtree[left] + segtree[right];
pos = pos/2;
}
}

public int sumRange(int i, int j) {
int left = i+n;
int right = j+n;
int result = 0;
while(left<=right){//范围未重合,等号表示父节点刚好表示左右的范围
if(left%2==1)//左边界是父节点的右孩子,那么其父节点的范围过大,所以不必将父节点累加,只需将其单独累加进结果,并缩小范围
{
result += segtree[left];
left++;
}
if(right%2==0)//右边界是父节点的左孩子,那么父节点的范围超出了查询范围,所以不考虑其父节点,单独将右边界的结果加入结果中
{
result += segtree[right];
right--;
}
left /= 2;//查询值其父节点,因为构建树时父节点包含了区域信息
right /=2;
}
return result;
}
}

/**
* Your NumArray object will be instantiated and called as such:
* NumArray obj = new NumArray(nums);
* obj.update(i,val);
* int param_2 = obj.sumRange(i,j);
*/
思路二:


(Sqrt decomposition)

就是预先划分出若干部分,提前算好和,加快速度

时空复杂度:

预处理:O(n) O(sqrt(N))

查询:O(sqrt(N)) O(1)

更新:  O(1)  O(1)

class NumArray {
//分而治之,没sqrt(n)为一块,预先统计出每一块的总和,加快速度
int[] block;
int len;
int[] num;
public NumArray(int[] nums) {
double l =Math.sqrt(nums.length);//计算每段的长度
len =(int)Math.ceil(nums.length/l);//九三一共需要的段数,需要向上取证,因为要包括所有的元素
block = new int[nums.length];
num = nums;
for(int i = 0;i<nums.length;i++){
block[i/len] += nums[i];
}
}

public void update(int i, int val) {
block[i/len] = block[i/len] + val - num[i];
num[i] = val;
}

public int sumRange(int i, int j) {
int result = 0;
int lblock = i/len;
int rblock = j/len;
if(lblock == rblock){
for(int a = i;a<=j;a++){
result += num[a];
}
return result;
}else{
for(int a = i;a<=(lblock+1)*len-1;a++){
result += num[a];
}
for(int a = lblock+1;a<rblock;a++){
result += block[a];
}
for(int a = rblock*len;a<=j;a++){
result += num[a];
}
return result;
}
}
}

/**
* Your NumArray object will be instantiated and called as such:
* NumArray obj = new NumArray(nums);
* obj.update(i,val);
* int param_2 = obj.sumRange(i,j);
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: