资讯专栏INFORMATION COLUMN

leetcode307. Range Sum Query - Mutable

Lemon_95 / 838人阅读

摘要:题目要求可以先参考数组不发生变动时的题目。最后的叶节点为当前数组的值,非叶结点则记录了子数组的范围以及该子数组中所有元素的和。它是一个非常轻量级的数据结构,而且几乎就是为这种题目量身打造。

题目要求
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.

可以先参考数组不发生变动时的题目。

这里的难度在于数组可以在中间出现变动,那么面对大容量数组的时候如何选择一个合适的数据结构及很重要。

思路一:map缓存

最开始我们有两种直观的想法,一种是在插入时同时更新后面所有的和,这意味着O(n)的插入复杂度和O(1)的读取复杂度。我决定选择第二种方式,也就是采用类似日志的形式记录下每一次的变更。这样当我们读取的时候,再遍历日志,将相关的变更结果添加到当前的数值上。缺点是,变更很多以及数组很庞大时,效率依然很差。

这个方法超时了。

private int[] sum;
    private int[] nums;
    private Map log;
    public NumArray(int[] nums) {
        this.nums = nums;
        sum = new int[nums.length];
        for(int i = 0 ; i();
    }
    
    public void update(int i, int val) {
        log.put(i, val - nums[i]);
    }
    
    public int sumRange(int i, int j) {
        int origin = 0;
        if(i==0) origin = sum[j];
        else origin = sum[j] - sum[i-1];
        for(Integer key : log.keySet()){
            if(key>=i && key <= j){
                origin += log.get(key);
            }
        }
        return origin;
    } 
思路二:Segment Tree

我们将一个数组转化为一棵树,其中当前的数组被均匀的分割并且分别用左子数组和右子数组构建左子树和右子树。最后的叶节点为当前数组的值,非叶结点则记录了子数组的范围以及该子数组中所有元素的和。

举个例子说明一下:
假设当前的数组为[1,2,5],则构成的Segment Tree为:

        8
       / 
      3   5
     / 
    1   2

这里先将[1,2,5]分割为[1,2][5]两个子数组,然后分别构造子树。最后的树如上所示。

    class SegmentTreeNode{
        int start;
        int end;
        SegmentTreeNode left;
        SegmentTreeNode right;
        int sum;
        
        public SegmentTreeNode(int start, int end){
            this.start = start;
            this.end = end;
        }
    }
    
    private SegmentTreeNode buildSegmentTree(int[] nums, int start, int end){
        if(start > end) return null;
        SegmentTreeNode cur = new SegmentTreeNode(start, end);
        if(start == end) cur.sum = nums[start];
        else{
            int mid = (start + end) / 2;
            cur.left = buildSegmentTree(nums, start, mid);
            cur.right = buildSegmentTree(nums, mid+1, end);
            cur.sum = cur.left.sum + cur.right.sum;
        }
        return cur;
    }
    
    private SegmentTreeNode root;
    public NumArray(int[] nums) {
        this.root = buildSegmentTree(nums, 0, nums.length-1);
    }
    
    public void update(int i, int val) {
        update(root, i, val);
    }
    
    private void update(SegmentTreeNode root, int position, int val){
        if(root.start == root.end){
            root.sum = val;
        }else{
            int mid = (root.start + root.end) / 2;
            if(position <= mid){
                update(root.left, position, val);
            }else{
                update(root.right, position, val);
            }
            root.sum = root.left.sum + root.right.sum;
        }
    }
    
    public int sumRange(int i, int j) {
        return sumRange(root, i, j);
    }    
    
    public int sumRange(SegmentTreeNode root, int i, int j){
        if(root.start==i && root.end==j){
            return root.sum;
        }
        int mid = (root.start + root.end )/2;
        if(j<=mid){
            return sumRange(root.left, i, j);
        }else if(i>mid){
            return sumRange(root.right, i, j);
        }else{
            return sumRange(root.left, i, mid) + sumRange(root.right, mid+1, j);
        }
    }

要想了解更多关于Segment Tree,请参考这篇文章。

思路三:Binary Indexed Tree

网上有非常多的关于二进制索引数树的教程。它是一个非常轻量级的数据结构,而且几乎就是为这种题目量身打造。可以先从这篇文章和这篇文章了解一下。

    class NumArray {
           int[] nums;
            int[] BIT;
            int n;

            public NumArray(int[] nums) {
                this.nums = nums;

                n = nums.length;
                BIT = new int[n + 1];
                for (int i = 0; i < n; i++)
                    init(i, nums[i]);
            }

            //每次更新当前节点的同时更新父节点
            public void init(int i, int val) {
                i++;
                while (i <= n) {
                    BIT[i] += val;
                    i += (i & -i);
                }
            }

            //更新当前节点,同时将改变传递给父节点
            void update(int i, int val) {
                int diff = val - nums[i];
                nums[i] = val;
                init(i, diff);
            }

            //
            public int getSum(int i) {
                int sum = 0;
                i++;
                while (i > 0) {
                    sum += BIT[i];
                    i -= (i & -i);
                }
                return sum;
            }

            public int sumRange(int i, int j) {
                return getSum(j) - getSum(i - 1);
            }
        }


想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注我的微信公众号!将会不定期的发放福利哦~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68694.html

相关文章

  • Range Sum Query

    摘要:表现在二进制上的规律每次加上最末尾的求末尾的就拿这个数和它的补码于。还有要求不是,要转换一下,和之前那道的思路差不多。这里用一个的表示从到的和。 Range Sum Query - Immutable Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), i...

    zhongmeizhi 评论0 收藏0
  • leetcode303-304 Range Sum Query Immutable

    摘要:假设有一个整数数组,计算下标从到包含和的数字的和。求和的请求将会在同一个整数数组上多次请求。这一题思路很简单,因为。而利用动态规划则很容易知道。这里将原先的一维数组替换成二维数组。要求计算一个矩形内的所有元素的值。 Range Sum Query Immutable Given an integer array nums, find the sum of the elements be...

    Worktile 评论0 收藏0
  • [LeetCode] 304. Range Sum Query 2D - Immutable

    Problem Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). https://leetcode.com/static/i...s...

    chinafgj 评论0 收藏0
  • [LeetCode] Range Sum Query Immutable

    Problem Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. Example Given nums = [-2, 0, 3, -5, 2, -1] sumRange(0, 2) -> 1 sumRange(2, 5) -> -1 sumRan...

    LiuZh 评论0 收藏0
  • 运用树状数组解决动态数组求和

    摘要:对于一组一维数组解决前项和,如果使用的方法需要的时间来找到前项数字的和,但是可以用的时间来更新对应数字的值但是仍然需要的时间来更新牵扯到相应数字数组的和,相反可以使用树状数组来降低运行时间求数组内一段数组的和,但同样我们增加了更新树状数组内 对于一组一维数组解决前n项和,如果使用linear scan的方法, 需要O(n)的时间来找到前n项数字的和,但是可以用O(1)的时间来更新对应数...

    Barrior 评论0 收藏0

发表评论

0条评论

Lemon_95

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<