字首和之樹狀陣列
樹狀陣列(Fenwick Tree)是一種用於高效處理區間查詢與修改的重要工具。它可以在 (O(log n)) 的時間複雜度內完成單點更新和字首區間求和的操作。
一、樹狀陣列的基本思想
樹狀陣列透過一個輔助陣列 (c[i]) 實現,將原陣列的資訊以一種特殊的方式儲存,使得查詢和更新都能快速完成。其核心思想是利用陣列的二進位制表示,按區間段儲存部分和。
例如,假設陣列下標從 (1) 開始:
- (c[i]) 儲存的是從 (i) 減去 (lowbit(i)) 再加 (1) 到 (i) 的區間和。這裡的 (lowbit(i)) 表示 (i) 的二進位制表示中最低位的 (1) 所對應的值,其計算方式是 (lowbit(x)=x) 與 (-x) 的按位與運算結果(即 (lowbit(x)=x & (-x)))。
二、樹狀陣列的結構特點
透過樹狀陣列,我們可以快速實現以下兩個操作:
(一)單點更新
更新陣列中的某個值,同時維護相關區間和。
(二)區間查詢
計算從起點到某點的字首和。
1. 示例:計算字首和
以陣列 (a[1..n]) 為例,假設使用樹狀陣列儲存資訊,我們可以用如下虛擬碼完成字首和計算:
// 查詢從索引1到指定index的字首和
public int query(int index) {
int res = 0;
while (index > 0) {
res += tree[index];
index -= index & -index;
}
return res;
}
2. 示例:單點更新
當我們想更新 (a[k]) 的值(例如加上 (y)),樹狀陣列中的相關節點也需要同步更新。具體實現如下:
// 對指定索引index處的元素進行單點更新,增加val的值
public void add(int index, int val) {
while (index <= n) {
tree[index] += val;
index += index & -index;
}
}
三、樹狀陣列的實際應用
樹狀陣列具有廣泛的實際應用,特別是在需要頻繁查詢和更新的場景中:
(一)區間求和問題
計算任意子區間的和。
(二)動態排序統計
例如,求解逆序對數。
(三)二維平面問題
如處理棋盤上的子矩陣求和。
程式碼實現
以下是樹狀陣列的基本實現程式碼:
public class BinaryIndexedTree {
// 表示樹狀陣列所處理的序列長度
private int n;
// 樹狀陣列本身
private int[] tree;
// 建構函式,用於初始化樹狀陣列,這裡假設傳入的陣列長度就是要處理的長度n
public BinaryIndexedTree(int[] arr) {
n = arr.length;
tree = new int[n + 1];
// 初始化樹狀陣列,可根據具體需求調整初始化邏輯
for (int i = 0; i < n; i++) {
add(i + 1, arr[i]);
}
}
// 查詢從索引1到指定index的字首和
public int query(int index) {
int res = 0;
while (index > 0) {
res += tree[index];
index -= index & -index;
}
return res;
}
// 對指定索引index處的元素進行單點更新,增加val的值
public void add(int index, int val) {
while (index <= n) {
tree[index] += val;
index += index & -index;
}
}
// 示例用法展示
public static void main(String[] args) {
int[] arr = {1, 3, 5, 7, 9};
BinaryIndexedTree bit = new BinaryIndexedTree(arr);
// 查詢索引為3的字首和
int prefixSum = bit.query(3);
System.out.println("索引為3的字首和: " + prefixSum);
// 對索引為2的元素進行單點更新,增加2
bit.add(2, 2);
// 再次查詢索引為3的字首和
prefixSum = bit.query(2);
System.out.println("更新後索引為2的字首和: " + prefixSum);
}
}