题目:
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.
思路:
本题目考查的是线段树或者二分索引树这两种数据结构。关于这两种数据结构的基本原理和典型应用我将会在随后的博客中给出详细解答(目前读者可以参考维基百科上关于这两种数据结构的介绍)。这里仅仅简单给出在这道题目中的实现和简单说明。
1、线段树(Segment Tree):线段树的基本原理如下:1)叶子结点存储输入的数据元素;2)每个内部结点(非叶子结点)表示某些叶子结点的合并(merge)。合并的方法可能会因问题而异(例如求和,求积,求最大值,最小值等等)。对于这个问题,合并指的是某个节点之下的所有叶子结点的和。线段树构造的时间复杂度是O(n),查询的时间复杂度是O(logn),更新的时间复杂度也是O(logn)。
2、二分索引树(Binary Indexed Tree):二分索引树的原理是利用数字的二进制性质,将索引数字转换成为二进制,按照其二进制从右往左第一个1所在位置代表的值将其分层(这篇帖子对二分索引树的原理介绍的比较直观:http://blog.csdn.net/qq508618087/article/details/51303552)。二分索引树构造的时间复杂度是O(nlogn),以后每次查询和更新的时间复杂度都是O(logn)。
代码:
1、线段树(Segment Tree):
class NumArray {
class SegmentTree {
class Node {
public:
int left, right;
int sum;
Node *lc, *rc;
Node(int s, int l, int r):sum(s), left(l), right(r)
{lc = rc = NULL;}
};
public:
SegmentTree(){}
SegmentTree(vector<int>& nums) {
root = build(nums, 0, nums.size() - 1);
}
~SegmentTree() {
freeSpace(root);
}
public:
int query(int low, int high) {
return query(root, low, high);
}
void update(int idx, int added) {
update(root, idx, added);
}
private:
Node *root;
void freeSpace(Node* root) {
if(root == NULL)
return;
freeSpace(root->lc);
freeSpace(root->rc);
delete root;
root = NULL;
}
Node* build(vector<int>& nums, int left, int right) {
if(left > right)
return NULL;
if(left == right)
return new Node(nums[left], left, right);
int mid = left + (right - left) / 2;
Node *ret = new Node(0, left, right);
ret->lc = build(nums, left, mid);
ret->rc = build(nums, mid+1, right);
ret->sum = ret->lc->sum + ret->rc->sum;
return ret;
}
int query(Node* root, int left, int right) {
if(root->left == left && root->right == right)
return root->sum;
if(right <= root->lc->right)
return query(root->lc, left, right);
else if(left >= root->rc->left)
return query(root->rc, left, right);
else {
int ret = query(root->lc, left, root->lc->right);
ret += query(root->rc, root->rc->left, right);
return ret;
}
}
void update(Node* root, int idx, int added) {
if(root == NULL)
return;
if(idx >= root->left && idx <= root->right)
root->sum += added;
if(root->left == root->right)
return;
if(idx <= root->lc->right)
update(root->lc, idx, added);
else if(idx >= root->rc->left)
update(root->rc, idx, added);
}
};
public:
NumArray(vector<int> nums) {
tree = new SegmentTree(nums);
this->nums = nums;
}
void update(int i, int val) {
int added = val - nums[i];
tree->update(i, added);
nums[i] = val;
}
int sumRange(int i, int j) {
return tree->query(i, j);
}
private:
SegmentTree *tree;
vector<int> nums;
};
// Your NumArray object will be instantiated and called as such:
// NumArray numArray(nums);
// numArray.sumRange(0, 1);
// numArray.update(1, 10);
// numArray.sumRange(1, 2);
2、二分索引树(Binary Indexed Tree):
class NumArray {
public:
NumArray(vector<int> nums) {
tem = nums;
bit.resize(nums.size() + 1, 0);
for(int i = 0; i < nums.size(); ++i) {
add(i + 1, nums[i]);
}
}
void update(int i, int val) {
add(i + 1, val - tem[i]);
tem[i] = val;
}
int sumRange(int i, int j) {
return sum(j + 1) - sum(i);
}
private:
void add(int index, int d) {
while(index <= tem.size()) {
bit[index] += d;
index += (index & -index);
}
}
int sum(int index) {
int ans = 0;
while(index > 0) {
ans += bit[index];
index -= (index & -index);
}
return ans;
}
vector<int> bit;
vector<int> tem;
};
/**
* 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);
*/