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

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:

  1. The array is only modifiable by the update function.
  2. 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);
 */


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值