传送门:[LeetCode] 307. 区域和检索 - 数组可修改
题目描述
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。
说明:
- 数组仅可以在 update 函数下进行修改。
- 你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。
示例 :
Given nums = [1, 3, 5]
sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8
分析与代码
解法一、暴力
- 就是普通数组,求和也是直接 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)
。