1. 程式人生 > >(原)數據結構之樹狀數組詳解

(原)數據結構之樹狀數組詳解

color 下一個 查詢 二進制 取反 date 其中 mce query

樹狀數組

樹狀數組是一個查詢修改復雜度都為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 }

(原)數據結構之樹狀數組詳解