樹狀數組(Binary Indexed Tree) 總結


1.“樹狀數組”數據結構的一種應用

  對含有n個元素的數組(a[1],...,a[k],...,a[n]):

  (1)求出第i個到第j個元素的和,sum=a[i]+...+a[j]。

    進行j-i+1次加法,復雜度為O(j-i+1)

  (2)任意修改其中某個元素的值。

    使用數組下標可以直接定位修改,時間復雜度為O(1)

   對於同時支持上述兩種操作的系統中,求和操作(1)求任意連續個數組元素和的平均時間復雜度為O(n),修改操作(2)時間復雜度是O(1)。如果系統中大量進行上述兩種操作m次,其中執行操作(1)概率1/p,操作(2)概率1-1/p,則系統時間復雜度為:

  可以使用樹狀數組使得上述兩種操作的時間復雜度為O(m*logn)

2.樹狀數組介紹

  核心思想:

    (1)樹狀數組中的每個元素是原數組中一個或者多個連續元素的和。

    (2)在進行連續求和操作a[1]+...+a[n]時,只需要將樹狀數組中某幾個元素的和即可。時間復雜度為O(lgn)

    (3)在進行修改某個元素a[i]時,只需要修改樹狀數組中某幾個元素的和即可。時間復雜度為O(lgn)

  下圖就是一個樹狀數組的示意圖:

  解釋如下:

  1) a[]: 保存原始數據的數組。(操作(1)求其中連續多個數的和,操作(2)任意修改其中一個元素)

    e[]: 樹狀數組,其中的任意一個元素e[i]可能是一個或者多個a數組中元素的和。如e[2]=a[1]+a[2]; e[3]=a[3]; e[4]=a[1]+a[2]+a[3]+a[4]。 

  2) e[i]是幾個a數組中的元素的和?

    如果數字 i 的二進制表示中末尾有k個連續的0,則e[i]是a數組中2^k個元素的和,則e[i]=a[i-2^k+1]+a[i-2^k+2]+...+a[i-1]+a[i]。

    如:4=100(2)  e[4]=a[1]+a[2]+a[3]+a[4];

      6=110(2)  e[6]=a[5]+a[6]

      7=111(2)  e[7]=a[7]

  3) 后繼:可以理解為節點的父親節點。離它最近的,且編號末位連續0比它多的就是的父親,如e[2]是e[1]的后繼;e[4]是e[2]的后繼。

      如e[4] = e[2]+e[3]+a[4] = a[1]+a[2]+a[3]+a[4] ,e[2]、e[3]的后繼就是e[4]。

      后繼主要是用來計算e數組,將當前已經計算出的e[i]添加到他們后繼中。

    前驅:節點前驅的編號即為比自己小的,最近的,最末連續0比自己多的節點。如e[7]的前驅是e[6],e[6]的前驅是e[4]。

        前驅主要是在計算連續和時,避免重復添加元素。

      如:Sum(7)=a[1]+...+a[7]=e[7]+e[6]+e[4]。(e[7]的前驅是e[6], e[6]的前驅是e[4])

    計算前驅與后繼:

      lowbit(i) = ( (i-1) ^ i) & i ;

      節點e[i]的前驅為 e[ i - lowbit(i) ];

      節點e[i]的前驅為 e[ i + lowbit(i) ]

3.樹狀數組代碼示例

 1 #include <iostream>
 2 #include <stdio.h>
 3 
 4 using namespace std;
 5 
 6 int input(int*,int*,int);    ///輸入數據
 7 int calStageSum(int*,int);    ///計算樹狀數組
 8 int getSum(int*,int);    ///求出前n個數字的和
 9 int updataElement(int*,int*,int,int,int);    ///更新某一位置上的元素
10 
11 int main (){
12     int n;
13     int newValue;
14     cout<<"Input the n(n>3) :";
15     cin>>n;
16 
17     int *num = new int[n+1];
18     int *sum = new int[n+1];
19 
20     cout<<"Input "<<n<<" numbers"<<endl;
21     input(num,sum,n);
22     calStageSum(sum,n);
23 
24     cout<<"The sum of first three number:"<<getSum(sum,3)<<endl;
25 
26     cout<<"Update the 2nd number value:";
27     cin>>newValue;
28     updataElement(sum,num,n,2,newValue);
29 
30     cout<<"The sum of first three number:"<<getSum(sum,3)<<endl;
31 
32     delete []num;
33     delete []sum;
34     return 0;
35 }
36 
37 int input(int* num,int *sum,int n){
38     for(int i=1;i<=n;i++){
39         cin>>num[i];
40         sum[i] = num[i];
41     }
42     return 0;
43 }
44 
45 int calStageSum(int *sum,int n){
46     int lowbit;
47     int par;
48     for(int i=1;i<=n;i++){
49         lowbit = ((i-1)^i)&i;
50         par = lowbit+i;   ///后繼節點id
51         if(par <= n){
52             sum[par] = sum[par] + sum[i];
53         }
54     }
55     return 0;
56 }
57 
58 int getSum(int* sum,int n){
59     int sumPreN = 0;
60     int lowbit  = 0;
61     while(n!=0){
62         sumPreN += sum[n];
63         lowbit = ((n-1)^n)&n;
64         n = n - lowbit; ///前驅節點id
65     }
66     return sumPreN;
67 }
68 
69 int updataElement(int* sum,int *num,int n,int pos,int newvalue){
70     int lowbit = 0;
71     int dis = newvalue - num[pos];
72     num[pos] = newvalue;
73     sum[pos] = sum[pos]+dis;
74 
75     while(true){
76         lowbit = ((pos-1)^pos)&pos;
77         pos = pos + lowbit; ///后繼節點id
78         if(pos <= n){
79             sum[pos] = sum[pos]+dis;
80         }
81         else
82             break;
83     }
84     return 0;
85 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM