(原)數據結構之樹狀數組詳解
樹狀數組
樹狀數組是一個查詢和修改復雜度都為log(n)的數據結構。
主要用於數組的單點修改&&區間求和.
另外一個擁有類似功能的是線段樹
??樹狀數組(Binary Indexed Tree) ------解決動態前綴和問題的數據結構
1、對於詢問操作(復雜度為logN)
查詢前綴和
(1)首先了解管轄區域:設節點編號為x,那麽這個節點管轄的區間為2^k,(其中k為二進制末尾0的個數)個元素;
什麽意思呢;
例:1)設我們一共有n個元素:a1,a2,a3,.........an,設管轄數組為d[ ] ;
則 d[6] = a5 + a6; 這是為什麽呢;
看6的二進制形式為110 末尾有一個零 ,則管轄的區間為2^1 = 2個元素
2)再看d[8] = a1+a2+....+a8; 共8個元素
因為8的二進制為1000 ,末尾有三個零,則管轄的區間為2^3 = 8 個元素;
以下面這幅圖為例
(2)了解如何查詢前綴和:
例:假設我們要查13這個位置的前綴和:
那麽怎麽知道為多少呢。
實際上就查詢這三部分,那麽這三部分是怎麽來的
將13 化為2進制
1101 = 13; 管轄2^0 = 1個元素
現將末尾的1去掉變成
1100 = 12;管轄2^2 = 4個元素;
再將末尾的1去掉變成
1000 = 8;管轄2^3 = 8個元素
加起來剛好也是13個元素;
實際上13的前綴和為d[13]+d[12]+d[8] (這裏也可以驗證一個區間內的元素不會超過logn個,所以單次查詢的復雜度最多為logn)
所以實際上如果要求這個我們就需要知道最低位的1在哪,將其減掉。
(3)了解lowbit函數(這是一個求最低位1的函數)
代碼寫起來非常簡單,如下:
1 int lowbit(int x) 2 3 { 4 5 return x&(-x); 6 7 }
為什麽是這樣寫呢:x與其負數;
首先從二進制的負數說起
在二進制中負數是以補碼形式存在的
例 假設我們求12的最低1的位
12 的二進制為 1100;
其負數為 取反+1 :0011+1 = 0100
將兩個相與變成 1100&0100 = 0100 便找到最低位的1 了;這樣我們就可以實現剛剛上面的過程了。
??所以上面的查詢功能的代碼可以這樣寫
代碼如下:
1 int lowbit(int x) 2 { 3 4 return x&(-x); 5 } 6 7 int Query(int x) 8 { 9 int ans = 0 ; 10 while(x) 11 { 12 ans += d[x]; 13 x -= lowbit(x); 14 }15 }
2、對於修改操作(復雜度為logN)(復雜度分析和上面一樣)
(單點修改)
其實是上面操作的逆操作
還是以上面那幅圖來說
假設我們要修改6,那麽對於應該修改圖中的標紅部分,應該都有貢獻
也就是圖中的6,8,16;那麽這是怎麽算出的呢,這其實是上面的逆操作;
以二進制來看,
6 = 110; 末尾的0為1個 所以,2^1 = 2;
6+2 = 110 + 010 = 1000 = 8;
8 的末尾0有2個, 所以 2^3 = 8 ;
所以下一個數為
1000+1000 = 8+8 = 16;
其實就是上面的逆過程;
所以修改6的話,應該修改的數有 6, 8,16;
代碼如下:
1 int lowbit(int x) 2 { 3 4 return x&(-x); 5 } 6 7 void Update(int x ,int value)//對a[x]加上value; 8 { 9 while(x<=n) 10 { 11 d[x] += value; 12 x += lowbit(x); 13 } 14 }
(原)數據結構之樹狀數組詳解