Binary Indexed Tree (树状数组)

对于Range Sum query这道题,我们除了用线段树去求解,树状数组一样也可以实现同样的功能,而且空间复杂度更小(线段树实际上需要O(2n)的空间,而树状数组的空间需求为O(n)),同时代码比线段树简单很多,唯一的问题在于架构逻辑难以理解

线段树基本上就是一个和原数组长度一样的查询数组,每一个奇数位下标(以1开始计算)都是和原数组的元素对应,每一个偶数位都是某几个位置元素的累积和,而对于BIT, 最重要的操作就是求最低位1的bit位操作,因为每一个位置的数字都和这个最低bit位操作的结果有关系,具体来说

  1. 每个下标的值就是每次减去下标最低位的1对应的值之后的一系列下标值的和,例如111为第7位,计算过程
7 - 7 & (-7) -> 6
6 - 6 & (-6) -> 4
4 - 4 & (-4) -> 0

所以第7位的值就是第7位原数组的值加上第6位的树状数组的值再加上第4位的树状数组的值,这里的第几位都是用的以1开始计算的下标,这里注意,树状数组的下标设置多加一个dummy node在0位这样会比较好算,有点像dp数组的情况

  1. 第二个问题就是当前位置的元素会对哪些位置的值产生影响,而这个问题同样可以利用最低位操作来解决,具体的,以1举例,计算过程如下
1 + 1 & (-1) -> 2
2 + 2 & (-2) -> 4
4 + 4 & (-4) -> 8

也就是说,第1位的值对第2位,第4位,第8位的值都产生了影响,明白了这两个基本原理,树状数组就可以得到实现了

class NumArray(object):

    def __init__(self, nums):
        """
        :type nums: List[int]
        """
        if nums:
            self.n = len(nums)
            self.array = [0] * (self.n + 1)
            self.nums = [0] * (self.n + 1)
            for i in xrange(self.n):
                self.update(i, nums[i])

    def update(self, i, val):
        """
        :type i: int
        :type val: int
        :rtype: void
        """
        diff = val - self.nums[i + 1]
        j = i + 1
        while j <= self.n:
            self.array[j] += diff
            j += (j & -j)
        self.nums[i + 1] = val

    def sumRange(self, i, j):
        """
        :type i: int
        :type j: int
        :rtype: int
        """
        return self.getSum(j + 1) - self.getSum(i)

    def getSum(self, i):
        result = 0
        j = i
        while j:
            result += self.array[j]
            j -= (j & -j)
        return result

这里始终需要注意,query时的i, j都是以0开始的坐标,而我们对nums和array数组都认为的加了一个dummy位,所以我们这里需要在query时注意边界问题

关于这个查询数组array的一些原理性的东西这里有篇博客写的比较清楚

  1. http://blog.csdn.net/int64ago/article/details/7429868

results matching ""

    No results matching ""