1. 程式人生 > >樹狀陣列與其應用(Python實現)(1)

樹狀陣列與其應用(Python實現)(1)

陣列-樹狀陣列

如果程式需要維護一個數組的**字首和**,S[i] = a[0]+ a[1] + …… + a[i-1]. 那麼一旦陣列中的一個元素 a[k]發生改變,則S[k+1] …… S[N] 都會發生變化(N是陣列長度)。

最壞情況下,字首和的更新需要O(N)時間,當n很大而陣列資料又經常變化時,程式的執行效率就會變得很低。

對於該類問題,運用樹狀陣列是一個不錯的選擇(另一種方法是線段樹

什麼是樹狀陣列
這裡寫圖片描述
如圖所示,對於樹狀陣列C有:
C[1] = A[1]
C[2] = A[2] + C[1]
C[3] = A[3]
C[4] = C[2] + C[3] + A[4] (A[4]的值為 4)
C[6] = C[5] + A[6] (A[6]的值為 6)
C[8] = C[7] + C[6] + C[4] + A[8]
……
這裡有一個有趣的性質: 若節點的編號為x,那麼這個節點所管轄的區間寬度為2^k,其中,k 時x的二進位制表示中末尾的0的個數:
eg:C[6] -> 6: 0110 -> k = 1 -> 他管理了2個數 A[5] 和A[6]
C[4] -> 4: 0100 ->k =2 -> 他管理了4個數(哪四個呢?讀者自己看一下咯)
Which means:
性質1:C[n] = A[n] + …… + A[ n - 2^k + 1 ]


那麼,問題來了,這個性質有什麼鳥用麼?
性質2:再看看,是不是對於每一個C[n]他的父親都是C[nn] ( nn=n + 2^k)
這兩個性質給樹狀陣列的操作帶來了很大的便利!!!

1. 修改元素的值:
假設,A[k]發生了變化,C中需要更新的陣列有哪些?能夠管的到A[k]的那些
首先,C[k]必然能夠管的到A[k],C[k]的父親,以及爺爺。。。。

def updata(index, x):
    while i<n:
        c[i] = c[i]+x
        i = i+ lowbit(i)
def lowbit(i):    #求i的二進位制數末尾0的格式k,並返回2^k
return i&(i^(i-1))

2. 求陣列A的前n項和(也就是在需要的情況下求字首和)
陣列A的前n項和是不是就是C[n]加上在它之前的同輩分的兄弟? have a think?

def query(i)
    sum =0;
    while n>0:
        sum = sum+ C[i]
        i = i - lowbit(i);
    return sum

* Ok 貼一下完整的PYHON程式碼*

:# -*- coding:utf-8 -*-

'''
function: 定義一個簡單的樹狀陣列類
author:hh
date:2016-5-26
note:宣告類例項時,引數為一個list
    要更新第k個值時,update(k,value)
    要求前k個元素的和,query(k-1)
'''
class treeArray(object): # lobit(i) = 2^k (k是i在二進位制中末尾0的個數) def lowbit(self,i): #return i&(-i) return i&(i^(i-1)); #更新值 def update(self,index,value): self.list_a[index] = self.list_a[index] + value; while index< len(self.list_a): self.list_c[index] = self.list_c[index]+value; #由於下標是從0開始,而計算lowbit時要從1開始計算,所以傳值index+1 index = index+ self.lowbit(index+1) #建構函式! def __init__(self,list_a): self.list_a = [0]*len(list_a); self.list_c = [0]*len(list_a) self.length = len(list_a) #print self.length for i in range(self.length): self.update(i,list_a[i]) #查詢index座標處的字首和 def query(self,index): sum = 0 if index >= self.length: print('ERRO,查詢範圍太大') return 0; while index >= 0: //大於等與0!!! sum = sum + self.list_c[index] index = index - self.lowbit(index+1) return sum #輸出此時的list_a(數值陣列) def output(self): k = self.length; print("list_a: ") print(self.list_a) return True

說明一下:由於list的下標從0開始,因此程式碼中的邊界條件需要注意

下面用一個例項來說明樹狀樹的應用:
敵兵佈陣問題:
有N個軍事基地,基地編號從0 到N-1,每個基地有不同數量的士兵,士兵數量可能發生增減,如何儘快的求出從第k個基地到第m個基地的士兵總數?

# -*- coding: utf-8 -*- 

from bin.treeArray import treeArray
'''
繼承了上面給出的樹狀陣列類
添加了方法:
add()
decrease()
AreaQuery()
'''
class sinPoiUpdateAreSum(treeArray):
    def __init__(self,list_a):
        treeArray.__init__(self,list_a)

#增加index基地的士兵人數
    def add(self,index, value):
        self.update(index,value);

      #減少index基地的士兵人數
    def decrease(self, index, value):
        self.update(index,-value)

#求a_index 到 b_index基地的士兵人數!
#顯然就是兩個字首和的差值!!
    def AreaQuery(self,a_index,b_index):
        return self.query(b_index) - self.query(a_index-1)

下一篇將利用樹狀陣列求解逆序對!!!Python新手,歡迎指正!!!

————————————————————————————————————— 2016-5-26
與 TJU-26 e