題目地址:
https://www.lintcode.com/problem/range-sum-query-mutable/description
給定一個數組,要求設計一個數據結構,支持區間和的查詢,以及單點修改。
樹狀數組(Fenwick Tree)是解決這個問題的經典數據結構。設原數組是,樹狀數組類似於前綴和數組,可以以的時間計算出的前個數之和;但與前綴和數組不同的是,它可以以的時間做單點更新(樹狀數組裏的單點更新的意思是,將原數組的第個位置的數增加,對應的樹狀數組做對應的更新),而對於前綴和數組來說,做單點更新的時候會花費的時間。具體可以參考https://blog.csdn.net/qq_46105170/article/details/103870987。代碼如下:
public class NumArray {
class FenwickTree {
private int[] tree;
public FenwickTree(int[] nums) {
// 樹狀數組和前綴和類似,需要開原數組長度多一個位置,
// 但多開一個位置的原因和前綴和數組不太一樣,它由樹狀數組本身的性質有關
tree = new int[nums.length + 1];
// 一開始樹狀數組對應的是元素全是0的數組,
// 初始化的時候其實就是做若干次單點更新,即將每個數字加進去
for (int i = 0; i < nums.length; i++) {
add(i + 1, nums[i]);
}
}
// 單點更新,將原數組的第i個數(從1開始數)增加d
public void add(int i, int d) {
while (i <= tree.length - 1) {
tree[i] += d;
i += lowbit(i);
}
}
// 求原數組前i個數的和(從1開始數)
public int sum(int i) {
int sum = 0;
while (i > 0) {
sum += tree[i];
i -= lowbit(i);
}
return sum;
}
private int lowbit(int x) {
return x & -x;
}
}
FenwickTree tree;
// 還需要把給定數組的引用存一下,
// 目的是,在做update的時候需要知道相比原數組增加的d是多少
int[] nums;
public NumArray(int[] nums) {
tree = new FenwickTree(nums);
this.nums = nums;
}
public void update(int i, int val) {
// 將nums[i]更新爲val,等價於將nums[i]增加val - nums[i],
// 對於樹狀數組來說,就是將第i + 1個數增加val - nums[i],
// 同時需要將原數組也做相應的變化,以便於下一次對樹狀數組做add操作
tree.add(i + 1, val - nums[i]);
nums[i] = val;
}
public int sumRange(int i, int j) {
// 返回原數組的第i + 1個數到第j + 1個數之和
return tree.sum(j + 1) - tree.sum(i);
}
}
預處理時間,區間和查詢和單點修改時間都是,空間。
註解:
類裏也可以不存原數組的引用,直接利用樹狀數組的sum方法也可以查到原數組某個位置的數,然後做update的時候就不需要更新原數組了。但這樣做的話時間會慢一點,因爲要多執行兩次sum操作。