给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。
示例:
Given nums = [1, 3, 5] sumRange(0, 2) -> 9 update(1, 2) sumRange(0, 2) -> 8
说明:
1.数组仅可以在 update 函数下进行修改。
2.你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。
分析:
这是道典型的线段树单点更新的题目,不懂线段树是不行的,线段树知识补充:
线段树的定义:线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,
实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。总之,线段树广泛用于求解区间问题。
线段树编写分为三个步骤:建树,更新,查询。
话不多说,直接上此题代码吧:
1 class NumArray { 2 public: 3 vector<int> tree;//把数据按树状分支存在数组中,建立一个树状数组 4 vector<int> num; 5 int k = 0;//测试用例初始数组遍历变量 6 int n;//测试用例初始数组数据个数 7 void pushup(int rt){ 8 tree[rt] = tree[rt<<1] + tree[rt<<1|1];//向上更新父结点值 9 } 10 void build(int l,int r,int rt){ //建树,rt为当前节点 11 if(l == r){ 12 tree[rt] = num[k++]; 13 return; 14 } 15 int mid = (l + r) / 2; 16 build(l,mid,rt<<1);//建左子树 17 build(mid + 1,r,rt<<1|1);//建右子树 18 pushup(rt); 19 } 20 NumArray(vector<int>& nums) { 21 n = nums.size(); 22 if(n != 0){ 23 num = nums; 24 tree = vector<int>(4 * n + 5,0);//注意申请4倍空间! 25 build(1,n,1);//从根节点开始建树 26 } 27 } 28 void UPDATE(int l,int r,int rt,int p,int val){ //更新p节点的值为val 29 if(l == r){ 30 tree[rt] = val; 31 return; 32 } 33 int mid = (l + r) / 2; 34 if(mid >= p) 35 UPDATE(l,mid,rt<<1,p,val); 36 else 37 UPDATE(mid + 1,r,rt<<1|1,p,val); 38 pushup(rt); 39 } 40 void update(int i, int val) { 41 UPDATE(1,n,1,i + 1,val); 42 } 43 int query(int L,int R,int l,int r,int rt){ //查询求 L 到 R 区间和 44 if(L <= l&&r <= R){ 45 return tree[rt]; 46 } 47 int mid = (l + r) / 2; 48 int sum = 0; 49 if(mid >= L) 50 sum += query(L,R,l,mid,rt<<1); 51 if(mid < R) 52 sum += query(L,R,mid + 1,r,rt<<1|1); 53 return sum; 54 } 55 int sumRange(int i, int j) { 56 if(n == 0) 57 return 0; 58 return query(i+1,j+1,1,n,1); 59 } 60 }; 61 62 /** 63 * Your NumArray object will be instantiated and called as such: 64 * NumArray* obj = new NumArray(nums); 65 * obj->update(i,val); 66 * int param_2 = obj->sumRange(i,j); 67 */