数组-树状数组
如果程序需要维护一个数组的**前缀和**,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)