题目地址:
https://leetcode.com/problems/range-sum-query-mutable/
给定一个数组,实现若干操作,每次操作是查询区间和,或者修改数组中某个元素的值。
法1:树状数组。原理可以参考https://blog.csdn.net/qq_46105170/article/details/103870987。代码如下:
class NumArray {
public:
#define lowbit(x) (x & -x)
int n;
vector<int> tr, A;
void add(int k, int x) {
for (; k <= n; k += lowbit(k)) tr[k] += x;
}
int sum(int k) {
int res = 0;
for (; k; k -= lowbit(k)) res += tr[k];
return res;
}
NumArray(vector<int>& nums) {
A.swap(nums);
n = A.size();
tr.resize(n + 1);
for (int i = 1; i <= n; i++) {
tr[i] += A[i - 1];
int j = i + lowbit(i);
if (j <= n) tr[j] += tr[i];
}
}
void update(int i, int val) {
add(i + 1, val - A[i]);
A[i] = val;
}
int sumRange(int l, int r) { return sum(r + 1) - sum(l); }
};
初始化时间复杂度 O ( n ) O(n) O(n),其余操作时间复杂度 O ( log n ) O(\log n) O(logn),空间 O ( n ) O(n) O(n)。
法2:线段树。其主要思想是,每个树的节点存一个区间的信息,在查询的时候只需要区间长度每次分块查询然后汇总即可。更新的时候,先递归到叶子节点更新叶子,然后一路递归上去更新沿途的节点。代码实现方面,由于线段树是个接近于满二叉树的树,所以可以用数组实现。具体请看代码,代码如下:
class NumArray {
public:
struct Node {
int l, r, sum;
};
vector<Node> tr;
vector<int> A;
void pushup(int u) { tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum; }
void build(int u, int l, int r) {
tr[u] = {l, r};
if (l == r) {
tr[u].sum = A[l - 1];
return;
}
int mid = l + (r - l >> 1);
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int i, int x) {
if (tr[u].l == tr[u].r) {
tr[u].sum = x;
return;
}
int mid = tr[u].l + (tr[u].r - tr[u].l >> 1);
if (i <= mid) modify(u << 1, i, x);
if (i > mid) modify(u << 1 | 1, i, x);
pushup(u);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
int res = 0;
int mid = tr[u].l + (tr[u].r - tr[u].l >> 1);
if (l <= mid) res = query(u << 1, l, r);
if (r > mid) res += query(u << 1 | 1, l, r);
return res;
}
NumArray(vector<int>& nums) {
A.swap(nums);
tr.resize(A.size() << 2);
build(1, 1, A.size());
}
void update(int index, int val) { modify(1, index + 1, val); }
int sumRange(int left, int right) { return query(1, left + 1, right + 1); }
};
所有操作时间空间复杂度都与树状数组一样。
一般而言,同样的题目,如果线段树和树状数组都可以做的话,树状数组的效率更高。但是树状数组适用的情景不如线段树多,所以两个数据结构各有优劣。本题两者都可以做。