传送门:[LeetCode] 307. 区域和检索 - 数组可修改

题目描述

给定一个整数数组 nums,求出数组从索引 ij (ij) 范围内元素的总和,包含 i, j 两点。

update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。

说明:

  • 数组仅可以在 update 函数下进行修改。
  • 你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。

示例 :

分析与代码

解法一、暴力

  • 就是普通数组,求和也是直接 for 循环。

代码:

class NumArray {
    private int[] data;

    public NumArray(int[] nums) {
        data = nums.clone();
    }

    public void update(int i, int val) {
        data[i] = val;
    }

    public int sumRange(int i, int j) {
        int sum = 0;
        while (i <= j) {
            sum += data[i++];
        }
        return sum;
    }
}

解法二、线段树

了解线段树

  • 线段树的构建、查询、单点更新。

代码:

class NumArray {
    private int[] data;
    private int[] tree;

    public NumArray(int[] nums) {
        if (nums.length == 0){
            return;
        }
        data = nums.clone();
        tree = new int[4 * nums.length];
        build(0, 0, nums.length - 1);
    }

    public void update(int i, int val) {
        data[i] = val;
        update(0, 0, data.length - 1, i, val);

    }

    public int sumRange(int i, int j) {
        return queryRange(0, 0, data.length - 1, i, j);
    }

    private void build(int treeIndex, int treeLeft, int treeRight) {
        if (treeLeft == treeRight) {
            tree[treeIndex] = data[treeLeft];
            return;
        }
        int leftChildIndex = getLeftChildIndex(treeIndex);
        int rightChildIndex = getRightChildIndex(treeIndex);
        int mid = (treeLeft + treeRight) >>> 1;
        build(leftChildIndex, treeLeft, mid);
        build(rightChildIndex, mid + 1, treeRight);
        tree[treeIndex] = tree[leftChildIndex] + tree[rightChildIndex];
    }

    private void update(int treeIndex, int treeLeft, int treeRight, int index, int val) {
        if (treeLeft == treeRight) {
            tree[treeIndex] = val;
            return;
        }
        int leftChildIndex = getLeftChildIndex(treeIndex);
        int rightChildIndex = getRightChildIndex(treeIndex);
        int mid = (treeLeft + treeRight) >>> 1;
        if (index > mid) {
            update(rightChildIndex, mid + 1, treeRight, index, val);
        } else {
            update(leftChildIndex, treeLeft, mid, index, val);
        }
        tree[treeIndex] = tree[leftChildIndex] + tree[rightChildIndex];
    }

    private int queryRange(int treeIndex, int treeLeft, int treeRight, int queryLeft, int queryRight) {
        if (queryLeft == treeLeft && queryRight == treeRight) {
            return tree[treeIndex];
        }
        int leftChildIndex = getLeftChildIndex(treeIndex);
        int rightChildIndex = getRightChildIndex(treeIndex);
        int mid = (treeLeft + treeRight) >>> 1;
        if (queryLeft > mid) {
            return queryRange(rightChildIndex, mid + 1, treeRight, queryLeft, queryRight);
        } else if (queryRight <= mid) {
            return queryRange(leftChildIndex, treeLeft, mid, queryLeft, queryRight);
        }
        int leftResult = queryRange(leftChildIndex, treeLeft, mid, queryLeft, mid);
        int rightResult = queryRange(rightChildIndex, mid + 1, treeRight, mid + 1, queryRight);
        return leftResult + rightResult;
    }

    private int getLeftChildIndex(int index) {
        return 2 * index + 1;
    }

    private int getRightChildIndex(int index) {
        return 2 * index + 2;
    }
}

小结

线段树查询和更新的时间复杂度都是O(logn),而普通数组的方法,更新的时间复杂度为O(1),查询为O(n)


02-11 00:28