算法基础篇(7)------树状数组

● 每周一言

健康,比什么都重要。

导语

树状数组顾名思义,是一个具有树结构的数组,其算法思路十分巧妙。树状数组能解决的问题集合基本上是线段树的子集,之所以后讲,是因为与线段树相比,树状数组的时空效率更高,代码实现也更简单且容易理解。那么,树状数组为什么具有这些优势?其算法又是如何实现的?

树状数组

树状数组可以在O(logN)时间效率下动态维护一个数列并查询其任意子序列的所有元素之和。之所以能稳定在O(logN),是利用了二进制的计算特性。下面我将通过这个特性具体介绍该算法的实现思路。

几个二进制数0011、0101、0110、0111,每次通过将原数的非零最低位加1来更新原数,我们得到0011→0100→1000→10000,0101→0110→1000→10000,0110→1000→10000,0111→1000→10000这几个变化过程。是不是发现了什么规律?没错,这种运算总能把原数变为一个2的n次方。我们不妨把这4个二进制数作为数组序列对应的下标3、5、6、7,上述变换用数组形式作图如下:

算法基础篇(7)------树状数组_第1张图片

上图中蓝色代表原数组,红色代表树状数组。红色单元从左至右的相连关系就是上面描述的“加1变换”(B1→B2→B4→B8,B3→B4→B8,B5→B6→B8,B7→B8)。有了这种结构,维护和查询任意子数列的和就很方便快捷了。只需要从头到尾遍历原数组,通过“加1变换”把每一个元素值累加到新数组相应的位置上,构造出树状数组,总的时间复杂度为O(NlogN)。这里假设原数列为[3,5,1,9,2,4,8,7],相应的树状数组如下图所示:
算法基础篇(7)------树状数组_第2张图片

上图中树状数组的各个红色节点值,都是通过“加1变换”累加原数列的值得到的结果,建议每个红色节点可以自己演算验证一下。

树状数组建好了,下面试试计算前7个数之和。我们通过“减1变换”来做,过程如下:0111→0110→0100→0000。树状数组对应下标的元素值为:8→6→18→0。可知前7个数和即为8 + 6 + 18 = 32,只需加2次。而且随着数列长度的增大,O(logN)的时间优势会变得更加明显。此外,当原数列中的某个值增加或减少d时,同样可以通过“加1变换”更新树状数组中相应的元素值,因此树状数组的维护成本仍然是O(logN)。

上面描述了树状数组的构建、查询和维护,这三部分都需要解决同一个关键问题,那就是“加/减1变换”。我们一般用lowbit表示一个二进制数的最低非零位,“加/减1变换”的计算公式:lowbit(x) = x and (x xor (x - 1))。利用补码特性,公式可以简化为:lowbit(x) = x and -x

以上,便是树状数组。敬请期待下节内容 最小生成树

结语

感谢各位的耐心阅读,后续文章于每周日奉上,欢迎大家关注小斗公众号 对半独白

算法基础篇(7)------树状数组_第3张图片

你可能感兴趣的:(算法基础系列)