链接:https://leetcode.com/problems/range-sum-query-mutable/
思路
Approach 1
类似Immutable的版本,同样记录从下标0~i间元素之和cache,不同在于还要记录原nums,并在update时逐个更新cache。sumRange()复杂度O(1),update()复杂度O(n)。还不如不cache,sumRange()时直接从i加到j。
Approach 2
Segment tree. A segment tree is a binary tree where each node represents an interval. Generally a node would store one or more properties of an interval which can be queried later. 记录线段上元素总和。sumRange() update()复杂度都是O(logn)
Approach 3
Binary indexed tree. 假设有一条重量不均匀的长面包,每次切下2的幂次方的长度,且小于上次切的长度。由于每步可能有多种切法,则形成了树形结构,按长度升序从左往右排列,每个结点代表了此次切下的长度及重量。虽然叫做树,但物理结构是数组,结点在树里的位置由数组的下标决定。设某元素BITree[i],A = i & -i,即i最低位的1及其后面的0组成的数,B = i - A,则BITree[i]表示此次切下的A个元素的总和,之前已切下了B个,即父结点BITree[B]。至于右边兄弟,这次切下了A,则最近的右兄弟(假设存在的话)切下了2A,即结点BITree[B+2A],剩下的右兄弟依此类推。
一个数i的二进制表示是唯一的,切出总长度i的切法也是唯一的,对应树里唯一的路径,想求长度i的总重,将路径上结点相加即可。而如果某点i的密度变化,则i对应的结点及其右边的兄弟重量都会变化,因为它们切掉的部分包含了点i。
构造BITree时,可以先视nums全为0,则BITree全为0,然后依次将nums[i]更新为真实值,并更新BITree。
代码
// Approach 1 212ms
class NumArray {
private:
vector<int> cache;
vector<int> vi;
public:
NumArray(vector<int>& nums) {
vi = nums;
int prev = 0;
for(auto num : nums) {
cache.push_back(prev += num);
}
}
void update(int i, int val) {
int interval = val - vi[i];
vi[i] = val;
for(; i < vi.size(); i++) {
cache[i] += interval;
}
}
int sumRange(int i, int j) {
return cache[j] - (i == 0 ? 0 : cache[i-1]);
}
};
/**
* 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);
*/
// Approach 2
class STreeNode {
public:
int l, r, sum;
STreeNode *left, *right;
STreeNode(int l, int r, int sum, STreeNode *left, STreeNode *right) : l(l), r(r), sum(sum), left(left), right(right) {}
~STreeNode() {
if(left) delete left;
if(right) delete right;
}
};
class NumArray {
private:
vector<int> vi;
STreeNode *root;
// 建立以线段[l, r]为根的线段树
STreeNode *buildTree(int l, int r) {
if(l == r) {
return new STreeNode(l, r, vi[l], nullptr, nullptr);
}
int mid = l + (r-l)/2;
// 建立左右子树
STreeNode *left = buildTree(l, mid),
*right = buildTree(mid+1, r);
return new STreeNode(l, r, left->sum + right->sum, left, right);
}
// 更新root树中所有包含i的线段结点
void updateTree(STreeNode *root, int i, int diff) {
if(!root) return;
int l = root->l, r = root->r;
if(i <= r && i >= l) {
root->sum += diff;
} else {
return;
}
int mid = l + (r-l)/2;
// i在左子线段内
if(i <= mid) updateTree(root->left, i, diff);
// i在右子线段内
else updateTree(root->right, i, diff);
}
int querySum(STreeNode *root, int i, int j) {
if(!root) return 0;
int l = root->l, r = root->r;
if(l == i && r == j) return root->sum;
int mid = l + (r-l)/2;
if(i > mid) return querySum(root->right, i, j);
if(j <= mid) return querySum(root->left, i, j);
return querySum(root->left, i, mid) + querySum(root->right, mid+1, j);
}
public:
NumArray(vector<int>& nums) {
vi = nums;
if(nums.empty()) return;
root = buildTree(0, nums.size() - 1);
}
void update(int i, int val) {
updateTree(root, i, val - vi[i]);
vi[i] = val;
}
int sumRange(int i, int j) {
return querySum(root, i, j);
}
};
/**
* 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);
*/
// Approach 3
class NumArray {
private:
vector<int> BITree;
vector<int> vi;
int prefixSum(int i) {
i++;
int sum = 0;
while(i) {
sum += BITree[i];
i -= i & -i;
}
return sum;
}
void updateBITree(int i, int diff) {
i++;
while(i <= vi.size()) {
BITree[i] += diff;
i += i & -i;
}
}
public:
NumArray(vector<int>& nums) {
vi = nums;
BITree = vector<int>(nums.size() + 1, 0);
for(int i = 0; i < nums.size(); i++) {
updateBITree(i, nums[i]);
}
}
void update(int i, int val) {
int diff = val - vi[i];
vi[i] = val;
updateBITree(i, diff);
}
int sumRange(int i, int j) {
return prefixSum(j) - prefixSum(i-1);
}
};
/**
* 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);
*/