在算法和数据结构的世界里,树状数组(Binary Indexed Tree,简称 BIT)是一种非常高效的数据结构,特别适合处理动态前缀和问题。如果你经常遇到需要频繁更新数组元素并查询前缀和的场景,那么树状数组将是你的好帮手!
本教程专为编程小白设计,我们将一步步讲解树状数组的基本原理,并用Python语言实现一个完整的树状数组类。即使你从未接触过这个概念,也能轻松上手。
树状数组是一种用于高效计算前缀和(Prefix Sum)的数据结构。它支持两种核心操作:
update(i, delta):将数组第 i 个位置的值增加 deltaquery(i):查询从第 1 个元素到第 i 个元素的和(即前缀和)这两种操作的时间复杂度都是 O(log n),比朴素方法(O(n))快得多!
树状数组利用了二进制表示的特性。每个位置 i 负责维护一段区间和,这段区间的长度由 i 的二进制最低位的 1 决定。
例如,数字 6 的二进制是 110,最低位的 1 在第 2 位(从右往左数,从 1 开始),所以 6 负责维护长度为 2^1 = 2 的区间。
为了快速找到最低位的 1,我们使用一个神奇的操作:i & -i。这在 Python 中可以直接使用。
下面是一个完整的树状数组类实现:
class FenwickTree: def __init__(self, size): """ 初始化树状数组 :param size: 数组大小(通常从1开始索引) """ self.n = size self.tree = [0] * (self.n + 1) # 索引从1开始,所以多分配一个位置 def update(self, index, delta): """ 更新操作:将index位置的值增加delta :param index: 要更新的位置(从1开始) :param delta: 增加的值 """ while index <= self.n: self.tree[index] += delta index += index & -index # 移动到父节点 def query(self, index): """ 查询前缀和:从1到index的和 :param index: 查询的结束位置(从1开始) :return: 前缀和 """ s = 0 while index > 0: s += self.tree[index] index -= index & -index # 移动到前一个区间 return s def range_query(self, left, right): """ 查询区间[left, right]的和 :param left: 左边界(从1开始) :param right: 右边界(从1开始) :return: 区间和 """ return self.query(right) - self.query(left - 1) 让我们通过一个简单例子来演示:
# 创建一个大小为5的树状数组ft = FenwickTree(5)# 初始数组假设为 [0, 0, 0, 0, 0]# 现在我们要把第3个位置设为10ft.update(3, 10)# 把第1个位置设为5ft.update(1, 5)# 查询前3个元素的和print(ft.query(3)) # 输出: 15# 查询区间[2, 4]的和print(ft.range_query(2, 4)) # 输出: 10(因为只有第3位有值) 关键在于 index & -index 这个位运算操作。它能快速找到当前索引对应的区间长度,从而在 O(log n) 时间内完成更新或查询。
例如,当 index = 6(二进制 110)时:
-index 在补码中是 ...1111111111010index & -index = 110 & 010 = 010 = 2这正好是 6 的最低位 1 所代表的数值。
树状数组在以下场景中非常有用:
通过本教程,你应该已经掌握了树状数组的基本概念和Python实现方法。这种数据结构虽然名字听起来有点吓人,但其实原理并不复杂,关键是理解其基于二进制的区间划分方式。
记住,树状数组最适合处理需要频繁进行单点更新和前缀和查询的问题。在实际编程竞赛或工程开发中,它往往是解决这类问题的首选工具之一。
现在,你可以尝试自己实现一个树状数组,或者将其应用到实际问题中去!
本文由主机测评网于2025-12-02发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://www.vpshk.cn/2025122111.html