[LeetCode] 303. 区域和检索 - 数组不可变

传送门:[LeetCode] 303. 区域和检索 - 数组不可变

题目描述

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

说明:

  • 你可以假设数组不可变。
  • 会多次调用 sumRange 方法。

示例 :

给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()

sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3

分析与代码

  • 直接求和是超时的,我们可以用数组保存前缀和。

解法一、前缀和数组

  • 创建一个长度为 nums.length + 1 的数组 sum,下标为 n 则保存前 n 个数的和。第一个为 0,最后一个为总和。
  • sumRange(i, j) 则返回 sum[j + 1] - sum[i]。sum[j + 1]保存下标 0 到 j 的数之和,而 sum[i] 保存下标 0 到 i - 1的数之和,相减则为下标 i 到 j 的数之和。

代码:

public class NumArray {
    private int[] sum;

    public NumArray(int[] nums) {
        sum = new int[nums.length + 1];
        for (int i = 0; i < nums.length; i++) {
            sum[i + 1] = sum[i] + nums[i];
        }
    }

    public int sumRange(int i, int j) {
        return sum[j + 1] - sum[i];
    }
}

解法二、线段树

了解线段树

  • 这题线段树只涉及构建和查询。

代码:

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 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 = getRightChileIndex(treeIndex);
        int mid = (treeLeft + treeRight) >>> 1;
        build(leftChildIndex, treeLeft, mid);
        build(rightChildIndex, mid + 1, treeRight);
        tree[treeIndex] = tree[leftChildIndex] + tree[rightChildIndex];
    }

    private int queryRange(int treeIndex, int treeLeft, int treeRight, int queryLeft, int queryRight) {
        if (treeLeft == queryLeft && treeRight == queryRight) {
            return tree[treeIndex];
        }
        int leftChildIndex = getLeftChildIndex(treeIndex);
        int rightChildIndex = getRightChileIndex(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 getRightChileIndex(int index) {
        return 2 * index + 2;
    }
}

小结

都用数组保存了累加和,空间换取时间。


posted @ 2019-10-08 20:00  Qiu_Jiaqi  阅读(195)  评论(0编辑  收藏  举报