【每日一题】力扣307.区域和检索-数组可修改(线段树实现)

题目描述(传送门)

给你一个数组 nums ,请你完成两类查询,其中一类查询要求更新数组下标对应的值,另一类查询要求返回数组中某个范围内元素的总和。

实现 NumArray 类:

NumArray(int[] nums) 用整数数组 nums 初始化对象
void update(int index, int val) 将 nums[index] 的值更新为 val
int sumRange(int left, int right) 返回子数组 nums[left, right] 的总和(即,nums[left] + nums[left + 1], …, nums[right])

示例:

输入:
["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); // 返回 9 ,sum([1,3,5]) = 9
numArray.update(1, 2);   // nums = [1,2,5]
numArray.sumRange(0, 2); // 返回 8 ,sum([1,2,5]) = 8

解题思路

线段树的保存

【每日一题】力扣307.区域和检索-数组可修改(线段树实现)_第1张图片

线段树的构造

【每日一题】力扣307.区域和检索-数组可修改(线段树实现)_第2张图片

线段树的求和

【每日一题】力扣307.区域和检索-数组可修改(线段树实现)_第3张图片

线段树的更新

【每日一题】力扣307.区域和检索-数组可修改(线段树实现)_第4张图片

代码实现


class NumArray {

    int[] value;
    int right_end; // 线段右端点
    public NumArray(int[] nums) {
        if (nums.length == 0) {
            return;
        }
        int n = nums.length * 4;// 一般线段树数组大小是原数组大小长度的4倍
        value = new int[n];
        build_segment_tree(value, nums, 0, 0, nums.length - 1);
        right_end = nums.length - 1;
    }

    public void update(int index, int val) {
        update_segment_tre(value, 0, 0, right_end, index, val);
    }

    public int sumRange(int left, int right) {
        return sum_range_segment_tree(value, 0, 0, right_end, left, right);
    }

    // 构建线段树
    //  value:线段树数组
    //  nums :原始数组
    //  pos  :当前线段(节点)在线段树数组(value)中的下标
    //  left :左端点
    //  right:右端点
    void build_segment_tree(int[] value, int[] nums, int pos, int left, int right) {
        if (left == right) {
            // 说明已经到达叶子节点
            value[pos] = nums[left];
            return;
        }
        // 计算线段中心
        int mid = left + (right - left) / 2;
        // 递归构建左子树线段
        build_segment_tree(value, nums, pos * 2 + 1, left, mid);
        // 递归构建右子树线段
        build_segment_tree(value, nums, pos * 2 + 2, mid + 1, right);
        // 计算value[pos] ,为左右子树代表的区间值的和。
        // 它的根节点下表为0,设某个节点的小标为i,它的左孩子下标为 2*i+1,右孩子小标为 2*i+2.
        value[pos] = value[pos * 2 + 1] + value[pos * 2 + 2];
    }

    //  线段树的求和
    int sum_range_segment_tree(int[] value, int pos, int left, int right, int qleft, int qright) {
        //  线段不相交
        if (qright < left || qleft > right) {
            return 0;
        }
        // 线段覆盖
        if (qleft <= left && qright >= right) {
            return value[pos];
        }
        int mid = left + (right - left) / 2;
        return sum_range_segment_tree(value, pos * 2 + 1, left, mid, qleft, qright) +
                sum_range_segment_tree(value, pos * 2 + 2, mid + 1, right, qleft, qright);
    }

    //  线段树的更新
    void update_segment_tre(int[] value, int pos, int left, int right, int index, int new_value) {
        if (left == right && left == index) {
            value[pos] = new_value;
            return;
        }
        int mid = left + (right - left) / 2;
        if (index <= mid) {
            update_segment_tre(value, pos * 2 + 1, left, mid, index, new_value);

        } else {
            update_segment_tre(value, pos * 2 + 2, mid + 1, right, index, new_value);

        }
        value[pos] = value[pos * 2 + 1] + value[pos * 2 + 2];
    }
}

你可能感兴趣的:(每日一题,算法,数据结构,leetcode,java)