307. Range Sum Query - Mutable
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.
方法0:
思路:
沿用306的方法,在每次update之后都相应改变后面prefix的值。那么update这一步的时间复杂度就是O(n)。如何进行优化呢?
方法1: Binary Indexed Tree / Fenwick Tree
Tushar: https://www.youtube.com/watch?v=CWDQJGaN1gY
huahua: https://www.youtube.com/watch?v=WbafSgetDDk
Fenwick Tree is mainly designed for solving the single point update range sum problems. e.g. what’s the sum between i-th and j-th element while the values of the elements are mutable.
Complexity of Fenwick Tree
Init the tree (include building all prefix sums) takes O(nlogn)
Update the value of an element takes O(logn)
Query the range sum takes O(logn)
Space complexity: O(n)
思路:
标准BIT的代码实现 + 307:
// huahua:http://zxi.mytechroad.com/blog/sp/fenwick-tree-binary-indexed-tree-sp3/
class FenwickTree {
private:
// low bit is to find the right most 1
// used for i += lowbit(i) to find next
// used for i -= lowbit(i) to find parent
static inline int lowbit(int x) {
return x & (-x);
}
vector<int> sums_;
public:
FenwickTree(int n): sums_(n + 1, 0) {}
void update(int i, int delta) {
while (i < sums_.size()) {
sums_[i] += delta;
i += lowbit(i);
}
}
int query(int i) const {
int sum = 0;
while (i > 0) {
sum += sums_[i];
i -= lowbit(i);
}
return sum;
}
};
class NumArray {
private:
vector<int> nums_;
FenwickTree tree_;
public:
NumArray(vector<int> nums): nums_(std::move(nums)), tree_(nums_.size()) {
for (int i = 0; i < nums_.size(); i++) {
tree_.update(i + 1, nums_[i]);
}
}
void update(int i, int val) {
tree_.update(i + 1, val - nums_[i]);
nums_[i] = val;
}
int sumRange(int i, int j) {
return tree_.query(j + 1) - tree_.query(i);
}
};
/**
* 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);
*/
方法2: segment tree
官方题解:https://leetcode.com/problems/range-sum-query-mutable/solution/
专职跑龙套: https://www.jianshu.com/p/91f2c503e62f (brilliant introduction of segment tree)
huahua:https://www.youtube.com/watch?v=rYBtViWXYeI
Segment tree is a very flexible data structure, because it is used to solve numerous range query problems like finding minimum, maximum, sum, greatest common divisor, least common denominator in array in logarithmic time.
下面这个是标准版的segment tree实现。
struct SegmentTreeNode {
int start;
int end;
int sum;
SegmentTreeNode* left;
SegmentTreeNode* right;
SegmentTreeNode(int start,
int end,
int sum,
SegmentTreeNode* left = nullptr,
SegmentTreeNode* right = nullptr):
start(start),
end(end),
sum(sum){}
};
class NumArray {
private:
SegmentTreeNode* root;
public:
NumArray(vector<int>& nums) {
int n = nums.size();
root = buildTree(nums, 0, n - 1);
}
void update(int i, int val) {
updateTree(root, i, val);
}
int sumRange(int i, int j) {
return queryTree(root, i, j);
}
SegmentTreeNode* buildTree(vector<int> & nums, int start, int end) {
if (start > end) return nullptr;
SegmentTreeNode* root = new SegmentTreeNode(start, end, 0);
if (start == end) {
root -> sum = nums[start];
return root;
}
int mid = start + (end - start) / 2;
root -> left = buildTree(nums, start, mid);
root -> right = buildTree(nums, mid + 1, end);
root -> sum = root -> left -> sum + root -> right -> sum;
return root;
}
int updateTree(SegmentTreeNode* root, int i, int val){
if (root == nullptr) return 0;
int diff;
if (root -> start == i && root -> end == i) {
diff = val - root -> sum;
root -> sum = val;
return diff;
}
int mid = root -> start + (root -> end - root -> start) / 2;
if (i <= mid) {
diff = updateTree(root -> left, i, val);
}
else {
diff = updateTree(root -> right, i, val);
}
root -> sum = root -> sum + diff;
return diff;
}
int queryTree(SegmentTreeNode * root, int i, int j) {
if (!root) return 0;
if (root -> start == i && root -> end ==j) return root -> sum;
int mid = root -> start + (root -> end - root -> start) / 2;
if (j <= mid) {
return queryTree(root -> left, i, j);
}
else if (i > mid) {
return queryTree(root -> right, i, j);
}
else return queryTree(root -> left, i, mid) + queryTree(root -> right, mid + 1, 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);
*/
// huahua: https://www.youtube.com/watch?v=rYBtViWXYeI
class SegmentTreeNode {
public:
int start;
int end;
int sum;
SegmentTreeNode* left;
SegmentTreeNode* right;
SegmentTreeNode(int start, int end, int sum,
SegmentTreeNode* left = nullptr,
SegmentTreeNode* right = nullptr):
start(start),
end(end),
sum(sum),
left(left),
right(right){}
SegmentTreeNode(const SegmentTreeNode &) = delete;
SegmentTreeNode & operator = (const SegmentTreeNode &) = delete;
~SegmentTreeNode() {
delete left;
delete right;
left = right = nullptr;
}
};
class NumArray {
private:
vector<int> nums_;
std::unique_ptr<SegmentTreeNode> root_;
SegmentTreeNode* buildTree(int start, int end) {
if (start == end) {
return new SegmentTreeNode(start, end, nums_[start]);
}
int mid = start + (end - start) / 2;
auto left = buildTree(start, mid);
auto right = buildTree(mid + 1, end);
return new SegmentTreeNode(start, end, left -> sum + right -> sum, left, right);
}
void updateTree(SegmentTreeNode* root, int i, int val) {
if (root -> start == i && root -> end == i) {
root -> sum = val;
return;
}
int mid = root -> start + (root -> end - root -> start) / 2;
if (i <= mid) {
updateTree(root -> left, i, val);
}
else {
updateTree(root -> right, i, val);
}
root -> sum = root -> left -> sum + root -> right -> sum;
}
int sumRange(SegmentTreeNode* root, int i, int j) {
if (i == root -> start && i == root -> end) {
return root -> sum;
}
int mid = root -> start + (root -> end - root -> start) / 2;
if (j <= mid) {
return sumRange(root -> left, i, j);
} else if (i > mid) {
return sumRange(root -> right, i, j);
} else {
return sumRange(root -> left, i, mid) + sumRange(root -> right, mid + 1, j);
}
}
public:
NumArray(vector<int>& nums) {
nums_.swap(nums);
if (!nums_.empty()){
root_.reset(buildTree(0, nums_.size() - 1));
}
}
void update(int i, int val) {
updateTree(root_.get(), i, val);
}
int sumRange(int i, int j) {
return sumRange(root_.get(), 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);
*/