Given an integer array nums, find the sum of the elements between indicesi 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
比较困难,主要是需要小于O(N)的时间复杂度。新的知识点,可以用树状数组(Binary Indexed Tree)或者线段树(Segment tree)来解决。
Binary Indexed Tree的特点 (from baidu): 每个节点x管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。求二进制最右边的1代表的数值可以用 (x & -x)。这样更新和求和都是log(N)的时间复杂度。
class NumArray {
public:
NumArray(vector<int> &nums) {
m_bitSums.resize(nums.size() + 1);
m_origNums.resize(nums.size() + 1);
for (int i = 0; i < nums.size(); ++i) {
update(i, nums[i]);
}
}
void update(int i, int val) {
int delta = val - m_origNums[++i];
m_origNums[i] = val;
while (i < m_bitSums.size()) {
m_bitSums[i] += delta;
i += (i & -i);
}
}
int sumRange(int i, int j) {
return (i == j) ? m_origNums[i+1] : getSum(j+1) - getSum(i);
}
private:
int getSum(int i) {
int result = 0;
while (i > 0) {
result += m_bitSums[i];
i -= (i & -i);
}
return result;
}
private:
vector<int> m_bitSums;
vector<int> m_origNums;
};
// 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);
From baidu - Segment tree是一种二叉搜索树,具有平衡的特点。它将一个区间分割成单元区间,对应叶节点。线段树是建立在线段的基础上,每个结点都代表了一条线段[a,b]。长度为1的线段称为元线段。非元线段都有两个子结点,左结点代表的线段为[a,(a + b) / 2],右结点代表的线段为[((a + b) / 2)+1,b]。
struct SegmentNode {
int start;
int end;
int sum;
SegmentNode* left;
SegmentNode* right;
SegmentNode(int s, int e)
: start(s), end(e), sum(0), left(nullptr), right(nullptr)
{
}
};
class NumArray {
public:
NumArray(vector<int> &nums) {
root = buildTree(nums, 0, nums.size() - 1);
}
void update(int i, int val) {
updateTree(root, i, val);
}
int sumRange(int i, int j) {
return sumTree(root, i, j);
}
private:
SegmentNode* buildTree(const vector<int>& nums, int start, int end) {
if (start > end) {
return nullptr;
}
SegmentNode* node = new SegmentNode(start, end);
if (start == end) {
node->sum = nums[start];
}
else {
int mid = start + (end - start) / 2;
node->left = buildTree(nums, start, mid);
node->right = buildTree(nums, mid + 1, end);
node->sum = node->left->sum + node->right->sum;
}
return node;
}
int updateTree(SegmentNode* root, int i, int val) {
if (root == nullptr || i < root->start || i > root->end) {
return 0;
}
if (root->start == i && root->end == i) {
int diff = val - root->sum;
root->sum = val;
return diff;
}
int mid = root->start + (root->end - root->start) / 2;
int diff = (i <= mid ? updateTree(root->left, i, val) : updateTree(root->right, i, val));
root->sum += diff;
return diff;
}
int sumTree(SegmentNode* root, int i, int j) {
if (root == nullptr || i > root->end || j < root->start) {
return 0;
}
if (i <= root->start && j >= root->end) {
return root->sum;
}
int mid = root->start + (root->end - root->start) / 2;
if (i > mid) {
return sumTree(root->right, i, j);
}
else if (j <= mid) {
return sumTree(root->left, i, j);
}
else {
return sumTree(root->left, i, mid) + sumTree(root->right, mid + 1, j);
}
}
private:
SegmentNode* root;
};
// 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);
BIT效率高一些,但个人感觉Segment Tree写起来简单一些,不熟悉BIT算法。