樹狀數組的原理和實現


樹狀數組的原理和實現

概念

樹狀數組或者二叉索引樹也稱作Binary Indexed Tree,又叫做Fenwick樹;它的查詢和修改的時間復雜度都是log(n),空間復雜度則為O(n),這是因為樹狀數組通過將線性結構轉化成樹狀結構,從而進行跳躍式掃描。通常使用在高效的計算數列的前綴和,區間和。

其中a數組就是原數組,c數組則是樹狀數組,可以發現

C1 = A1
C2 = A1+A2
C3 = A3
C4 = A1+A2+A3+A4
C5 = A5
C6 = A5+A6
C7 = A7
C8 = A1+A2+A3+A4+A5+A6+A7+A8

原理

lowbit

它通過公式來得出k,其中k就是該值從末尾開始0的個數。然后將其得出的結果加上x自身就可以得出當前節點的父親節點的位置或者是x減去其結果就可以得出上一個父親節點的位置。比如當前是6,二進制就是0110,k為2,那么6+2=8,而C(8)則是C(6)的父親節點的位置;相反,6-2=4,則是C(6)的上一個父親節點的位置。

def LOWBIT(x):
    return x & (-x)

注意:LOWBIT無法處理0的情況,因為它的結果也是0,那么最終就是一個死循環

單點修改

當我們要對最底層的值進行更新時,那么它相應的父親節點存儲的和也需要進行更新,所以修改的代碼如下:

def MODIFY(x, delta):
    if x < 1:
        return
    while x <= n:
        fenwick[x] += delta
        x += LOWBIT(x)

查詢

而查詢的時候,則需要向前進行統計

def QUERY(x):
    result = 0
    while right > 0:
        result += fenwick[x]
        x -= LOWBIT(x)
    return result

例如

15=(1111)2,通過lowbit分解,它可以變成4個數的和:(1111)2=(1)2+(10)2+(100)2+(1000)2,然后我們分析這個倒着跳的過程。減去15的最小的2的冪次2^0得到14。減去14的最小的2的冪次2^1得到12。減去12的最小的2的冪次2^2得到8。減去8的最小的2的冪次2^3得到0。

所以C(15) = C(14) + C(12) + C(8) + C(0),由圖也可以得知,其結果是正確的。

除此之外,樹狀數組能夠快速的求任意區間的和,設sum(k) = A[1] + A[2] + ... + A[k],則A[i] + A[i+1] + ... + A[j] = sum(j) - sum(i-1)。

練習

輸入n個位置,然后先按照y的順序,如果相等則按照x的順序,最終求該坐標左下的星星的數目,網址,根據題目要求,y已經滿足要求,那么只需要考慮x即可,那么我們就可以使用樹狀數組來計算前面的星星的數目。

#include<stdio.h>
#include<string.h>
int c[32000+10];
int a[15000+10];
int lowbit(int x)
{
    return x&(-x);
}
void updata(int x,int d)
{
    while(x<=32001)
    {
        c[x]=c[x]+d;
        x=x+lowbit(x);
    }
}
int getsum(int x)
{
    int res = 0;
    while(x>0)
    {
        res=res+c[x];
        x=x-lowbit(x);
    }
    return res;
}
int main()
{
    int n;
    int i,x,y;
    while(scanf("%d",&n)!=EOF)
    {
        memset(c,0,sizeof(c));
        memset(a,0,sizeof(a));
        for(i=0; i<n; i++)
        {
            //因為y是升序,所以橫坐標小於x的,(想了很久)所有點都符合,這是解這道題的關鍵。
            scanf("%d%d",&x,&y);    //下標可能從0開始,所以要x+1
            a[getsum(x+1)]++;       //求出橫坐標小於x的所有stars個數,並記錄到a中
            updata(x+1,1);          //更新區間
        }
        for(i=0; i<n; i++)
        {
            printf("%d\n",a[i]);
        }
    }
    return 0;
}

優缺點

樹狀數組的優點:

  1. 代碼短小,實現簡單;
  2. 容易擴展到高緯度的數據;

缺點:

  1. 只能用於求和,不能求最大/小值;
  2. 不能動態插入;
  3. 數據多時,空間壓力大;


免責聲明!

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



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