Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.
The update(i, val) function modifies nums by updating the element at index i to val.
Example:
Given nums = [1, 3, 5]
sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8
Note:
- The array is only modifiable by the update function.
- You may assume the number of calls to update and sumRange function is distributed evenly.
======================2019.1.24==========================
节点版本,可以在每个节点维护区间和,还有区间最大值。
class NumArray {
class SegmentTree{
SegmentTree left;
SegmentTree right;
int sum;
}
int[] nums;
SegmentTree root;
public NumArray(int[] nums) {
this.nums=nums;
root=build(0,nums.length-1);
}
public SegmentTree build(int l,int r){
if(l>r) return null;
if(l==r){
SegmentTree node=new SegmentTree();
node.sum=nums[l];
return node;
}
int m=(l+r)/2;
SegmentTree node=new SegmentTree();
node.left=build(l,m);
node.right=build(m+1,r);
node.sum=(node.left==null?0:node.left.sum)+(node.right==null?0:node.right.sum);
return node;
}
public void update(int i, int val) {
int dif=nums[i]-val;//需要减去
nums[i]=val;
update(root,i,0,nums.length-1,dif);
}
void update(SegmentTree node,int i,int l,int r,int dif){//root代表[0,nums.length-1]区间
if(node==null) return;
node.sum-=dif;
if(l==r) return;
int m=(l+r)/2;
if(i>m){
update(node.right,i,m+1,r,dif);
}else{
update(node.left,i,l,m,dif);
}
}
public int sumRange(int l, int r) {
return sumRange(root,l,r,0,nums.length-1);
}
int sumRange(SegmentTree node,int l,int r,int ll,int rr){
int mid=(ll+rr)/2;
if(l==ll&&r==rr) return node.sum;
int res=0;
if(l<=mid&&r>mid) res+=sumRange(node.left,l,mid,ll,mid)+sumRange(node.right,mid+1,r,mid+1,rr);
else if(r<=mid){
res+=sumRange(node.left,l,r,ll,mid);
}else if(l>mid){
res+=sumRange(node.right,l,r,mid+1,rr);
}
return res;
}
}
========================================================
======================2019.1.23==========================
class NumArray {
int[] nums;
int[] sum;
public NumArray(int[] nums) {
this.nums=nums;
sum=new int[nums.length*2];
for(int i=nums.length;i<sum.length;i++){
sum[i]=nums[i-nums.length];
}
for(int i=nums.length-1;i>0;i--) sum[i]=sum[i*2]+sum[i*2+1];
}
public void update(int i, int val) {
int index=i+nums.length;
int dif=nums[i]-val;
nums[i]=val;
while(index>0){
sum[index]-=dif;
index/=2;
}
}
public int sumRange(int i, int j) {
int l=i+nums.length,r=j+nums.length;
int res=0;
while(true){
if(r%2==0){
res+=sum[r];
r--;
}
if(l%2==1){
res+=sum[l];
l++;
}
if(l>r) break;
l=l/2;
r=r/2;
}
return res;
}
}
========================================================
参考https://blog.youkuaiyun.com/zearot/article/details/52280189 写了线段树,但看到leetcode参考答案还有更高效的写法(只需开辟2n数组长度,左右子节点区间不一定相同,自底向上迭代,能快速定位的叶子节点),待改进。
这里的线段树思路用sum[ ]保存各个区间和。sum一个下标对应一个区间。
class NumArray {
int[] sum,nums;
public NumArray(int[] nums) {
if(nums==null||nums.length==0){
this.nums=nums;
return;
}
int len=nums.length;
this.nums=nums;
sum=new int[len*4];
build(1,0,len-1);
}
//n为sum的下标,区间[l,r]对应n,构造线段树
private void build(int n,int l,int r){
if(l==r){
sum[n]=nums[l];
return;
}
int m=(l+r)/2;
build(n*2,l,m);
build(n*2+1,m+1,r);
sum[n]=sum[2*n]+sum[2*n+1];
}
public void update(int i, int val) {
update(i,val-nums[i],1,0,nums.length-1);
nums[i]=val;
}
private void update(int index,int v,int n,int l,int r) {//l,r区间,index下标,v增加值,n区间对应的sum下标
sum[n]+=v;
if(l==r) return;
int m=(l+r)/2;
if(index>m){//在右边区间,区间对应index
update(index,v,2*n+1,m+1,r);
}else{
update(index,v,2*n,l,m);
}
}
public int sumRange(int i, int j) {
return count(i,j,1,0,nums.length-1);
}
private int count(int i,int j,int index,int l,int r){
if(i==l&&j==r){
return sum[index];
}
int m=(l+r)/2;
if(i>m){//在右边
return count(i,j,index*2+1,m+1,r);
}
if(j<=m){//在左边
return count(i,j,index*2,l,m);
}
//在两边
return count(i,m,index*2,l,m)+count(m+1,j,index*2+1,m+1,r);
}
}
/**
* Your NumArray object will be instantiated and called as such:
* NumArray obj = new NumArray(nums);
* obj.update(i,val);
* int param_2 = obj.sumRange(i,j);
*/
改进:参考https://leetcode.com/problems/range-sum-query-mutable/solution/
这样build线段树,树中的线段元素(比如共有5个元素的情况)可能有不连续的情况,但能节省空间。因为能定位到某个点,这是一种自底向上的方式,并没有上面显式的有一个sum的一个下标对应一个区间。
class NumArray {
int[] nums,sum;
int len;
public NumArray(int[] nums) {
this.nums=nums;
len=nums.length;
sum=new int[len<<1];
//build树
for(int i=len;i<len<<1;i++){
sum[i]=nums[i-len];
}
for(int i=len-1;i>0;i--){
sum[i]=sum[i<<1]+sum[i<<1|1];//左子节点偶数,右子节点奇数
}
}
public void update(int i, int val) {
int k=val-nums[i];
nums[i]=val;
int n=i+len;
while(n>0){
sum[n]+=k;//它的根节点都要更新
n=n/2;
}
}
public int sumRange(int i, int j) {
int l=i+len,r=j+len;
//左子节点偶数,右子节点奇数
int s=0;
while(l<=r){
if(l%2==1){
s+=sum[l];
l++;
}
if(r%2==0){
s+=sum[r];
r--;
}
l=l/2;
r=r/2;
}
return s;
}
}
/**
* Your NumArray object will be instantiated and called as such:
* NumArray obj = new NumArray(nums);
* obj.update(i,val);
* int param_2 = obj.sumRange(i,j);
*/