[LeetCode]Range Sum Query

本文介绍了解决区间和查询及更新问题的两种高效数据结构——树状数组和线段树。通过具体的Java实现代码,详细解析了这两种数据结构的工作原理及其在LeetCode题目中的应用。

https://leetcode.com/problems/range-sum-query-mutable/

求i ~ j位置和

由于题干中说sum和update操作频率相同,区间求和考虑两种做法:树状数组和线段树。


树状数组:

理解关键在明白树状数组的结构图,



理解如何求sum和update的过程主要是

// 就是把k的二进制的高位1全部清空,只留下最低位的1
(i & -i)
init和update函数传入的都是index和这个index要更新的值,BIT多开一位,所以相关i都要先自增一位。初始化update的时候一直加,上界是n;查询getSum是一直减,下界是0。


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


    public NumArray(int[] nums) {
        this.nums = nums;
        n = nums.length;
        BIT = new int[n + 1];
        for (int i = 0; i < nums.length; i++) {
            init(i, nums[i]);
        }
    }
    
    private void init(int i, int val) {
        // 由于BIT长度为n+1,所以每次都要先i++一下
        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 sumRange(int i, int j) {
        return getSum(j) - getSum(i - 1);
    }
    
    private int getSum(int i) {
        i++;
        int sum = 0;
        while (i > 0) {
            sum += BIT[i];
            i -= (i & -i);
        }
        return sum;
    }
}



线段树:

节点内有beg、end两个参数表示该节点所表示的sum的范围是从beg到end。显然当beg==end时,sum=nums[beg]。也就是说,线段树中叶子节点表示nums[i]的值,非叶子结点表示一个区间和。sum和update显然也是要用到递归操作(毕竟是树)。

节点的左子树是beg~mid范围的和,右子书是mid+1到end范围的和。


public class NumArray {

    class SegmentTreeNode {
        int start, end;
        SegmentTreeNode left, right;
        int sum;

        public SegmentTreeNode(int start, int end) {
            this.start = start;
            this.end = end;
            this.left = null;
            this.right = null;
            this.sum = 0;
        }
    }
      
    SegmentTreeNode root = null;
   
    public NumArray(int[] nums) {
        root = buildTree(nums, 0, nums.length-1);
    }

    private SegmentTreeNode buildTree(int[] nums, int start, int end) {
        if (start > end) {
            return null;
        } else {
            SegmentTreeNode ret = new SegmentTreeNode(start, end);
            if (start == end) {
                ret.sum = nums[start];
            } else {
                int mid = start  + (end - start) / 2;             
                ret.left = buildTree(nums, start, mid);
                ret.right = buildTree(nums, mid + 1, end);
                ret.sum = ret.left.sum + ret.right.sum;
            }         
            return ret;
        }
    }
   
    void update(int i, int val) {
        update(root, i, val);
    }
   
    void update(SegmentTreeNode root, int pos, int val) {
        if (root.start == root.end) {
           root.sum = val;
        } else {
            int mid = root.start + (root.end - root.start) / 2;
            if (pos <= mid) {
                 update(root.left, pos, val);
            } else {
                 update(root.right, pos, 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 start, int end) {
        if (root.end == end && root.start == start) {
            return root.sum;
        } else {
            int mid = root.start + (root.end - root.start) / 2;
            if (end <= mid) {
                return sumRange(root.left, start, end);
            } else if (start >= mid+1) {
                return sumRange(root.right, start, end);
            }  else {    
                return sumRange(root.right, mid+1, end) + sumRange(root.left, start, mid);
            }
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值