树状数组 原理及分析

     比较心虚,因为并不知道这是不是真正的所谓原理,最初的解释来自于知乎的某个大神,后来只是自己稍微完善了一小步,做了一下复杂度的分析啥的,美其名曰是原理的分析,倒不如说是我的一些关于树状数组的小想法。

    想写这篇文章的主要原因:网上的博客不少树状数组的教程都会让你去理解一个叫Lowbit的函数,一开始我也是这么做的,后来也是觉得很好用,理所当然地认为lowbit之后就秒天秒地可以了,后来在学其他数据结构的时候需要用到树状数组,发现自己并不是很明白为什么要lowbit,百度+yy之后就有了这一篇文章。

 

(大佬和赶时间的小伙儿可以直接看加粗部分)

 

 言归正传。讲树状数组,首先需要的是一张图:

 

既然是想要知道原理,而这个图只是一个结果,那咱们就先扔掉这一张图。

 

    首先,大家考虑为什么要用树状数组。因为大家需要解决多次询问区间和以及点更新的问题。首先从朴素的方法入手。朴素的方法也就是用一个简单的数组,然后更新用 1 个操作,查询用n个操作。这时候我们想,我的我的查询好麻烦,学过DP的小伙伴们再被这种题目卡到的时候一般会想到一种做法就是求得前缀和。可是这样修改的复杂度又上去了。 仔细想想发现,如果我把dp的思想和修改结合一下貌似是可以的一种做法,就是难以把握dp和这个数组的平衡点。

    发明这个数据结构的大神或许是这么想的: 如果我把奇数项都加到后面的偶数项上,得到一个新的序列,我求区间和的时候,复杂度就会降低到原来的一般,而如果我恰好更新到了某个奇数,我只要顺便把后面的数字一起更新了就好。也就是说,用更新的+1  换取了查询的/2。 交换稳赚不赔~ 只是变为二分之一依旧不够,就对得到的偶数序列递归操作。递归到某一层之后让总体的复杂度最低我最开心。

咱们可以假设,一共递归了m层,对于一个问题询问和查询的次数都是一样的。下面不严谨地证明一下递归的最佳层数。

假设一共递归了m层,那么我每一次更新需要的操作平均操作数为 (m + 1) 


/*
	我的查询操作数平均为 (len / 2) / (2 ^ m) 
	平均操作数最小的时候应该是保证 (m + 1) + (len / 2) / (2 ^ m) 最小。 
	t = m + 1 换元之后求导, 可得当 2 ^ (m + 1) == ln2 * len 的时候取得最小值。
*/

说明了,当层数达到log(len)的时候,操作数接近最小。均摊复杂度可以达到 log(len) 正好是我们想要的。

    证明算是很不严谨。在更新的平均操作数的时候没有仔细计算过一个平均的值,不过我们用来粗略估算复杂度就够了。这时候我们就找到了让均摊复杂度变为logn的方法。然后回头看那张图,大概就明白图为什么这么奇怪了。


    同时,这种思想也可以解释奇怪的lowbit函数.这种做法主要是找当前数字右边的那个偶数,递归调用时联系一下位运算, 就会发现,其实是吧最后一位 1 加上1 实行进位。 位运算中的取补操作 然后再与原数字取and 可以拿到的数字和我们需要的加数,然后加上原数字就是我们这个数字需要到的位置。 就得到了

                                num += num & (-num);

这样的形式。这就是插入。

    而在查询的时候,最后一个1就代表了最后一个长度为1000……n0)的小团子。想要得到1-n的和还要再算上团子之前的数字。 也就是

                                num -= num & (-num);

 

    手拙,就不给出代码了,而且思想简单剩下的代码实现应该也就是一小会儿的事情。(关键是我很懒啦,也懒得找题目了233


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值