传送门:[LeetCode] 303. 区域和检索 - 数组不可变
题目描述
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 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;
}
}
小结
都用数组保存了累加和,空间换取时间。