树状数组与其应用(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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值