题意:给定一个整数数组nums,找到数组小标为i,j(i小于j且包含)之间的元素的和,同时update(i,val)函数将下标为i的元素的值该为val
声明:1数组的值只能被update函数修改;2 update和sumRange函数调用分布均匀。
思路:使用数据结构segment tree, 节点保存区间、区间的和,以及覆盖标志,更新操作O(logn),求和O(logn)
注意问题: int nums[] = new int[0] 此时数组nums不为null且长度为0;而int nums[] 只声明引用类型的变量,则nums为null。必须判断nums.length == 0,具体解释参考链接http://coolshell.cn/articles/11377.html
public class NumArray {
Node root;
int[] nums;
static class Node{
int sum;
int leftIndex;
int rightIndex;
Node leftChild; //null
Node rightChild; //null
public Node(int left, int right) {
super();
this.leftIndex = left;
this.rightIndex = right;
sum = 0;
}
}
public NumArray(int[] nums) {
this.nums = nums;
if(nums == null || nums.length == 0)
return;
root = buildSegmentTree(0, nums.length-1, nums);
}
private static Node buildSegmentTree(int left, int right, int[] nums) {
Node node = new Node(left, right);
if(right - left == 0){
node.sum = nums[left]; //leaf node composed of length 1
node.leftChild = null;
node.rightChild = null;
}else{
int mid = (left + right)/2;
node.leftChild = buildSegmentTree(left, mid, nums); //intermediate node
node.rightChild = buildSegmentTree(mid+1, right, nums); //non-interactive
node.sum = node.leftChild.sum + node.rightChild.sum;
}
return node;
}
void update(int i, int val) {
assert(i >= 0 && i < nums.length);
int delta = val - nums[i];
nums[i] = val;
update(root, i, delta);
}
private void update(Node root, int i, int delta) {
root.sum += delta;
if(root.rightIndex - root.leftIndex == 0)
return;
int mid = (root.rightIndex + root.leftIndex)/2;
if(i <= mid){
update(root.leftChild, i, delta);
} else{
update(root.rightChild, i, delta);
}
}
public int sumRange(int i, int j) {
int sum = 0;
if(i == j) return nums[i];
sum += sumRange(root, i, j);
return sum;
}
private int sumRange(Node root, int i, int j) {//time:O(logn)
if(i == root.leftIndex && j == root.rightIndex){
return root.sum;
}
int mid = (root.rightIndex + root.leftIndex)/2;
if(i > mid){
return sumRange(root.rightChild, i, j);
}
if(j < mid+1)
return sumRange(root.leftChild, i, j);
else{
return sumRange(root.leftChild, i, mid) + sumRange(root.rightChild, mid+1, j);
}
}