算法--树状数组

引入问题:力扣题目

给出一个长度为n的数组,完成以下两种操作多次:1、将下标为index的元素的值加上k(k可以是正数也可以是负数) 2、输出区间[left, right]内所有元素的和(包含边界)

朴素算法:对区间的元素累加求和,复杂度O(n^2),单点修改复杂度O(1),区间查询复杂度O(n)

前缀和:复杂度O(n^2),单点修改复杂度O(n),因为修改完数组值后还要修改前缀树组的值,区间查询复杂度O(1)

可以看出以上两种方法时间复杂度都比较高,而用树状数组可以有效的降低时间复杂度。

树状数组:树状数组二叉索引树(英语:Binary Indexed Tree),又以其发明者命名为Fenwick树,最早由Peter M. Fenwick于1994年以A New Data Structure for Cumulative Frequency Tables为题发表在SOFTWARE PRACTICE AND EXPERIENCE。其初衷是解决数据压缩里的累积频率(Cumulative Frequency)的计算问题,现多用于高效计算数列的前缀和, 区间和。(复杂度O(nlogn),单点修改复杂度O(logn),区间查询复杂度O(logn)).

前置知识:lowbit函数

lowbit函数即求一个非负整数在二进制表示下最低位1和后面的0所构成的十进制值。如lowbit(18) = 2(18的二进制表示为0b10010, 0b10的十进制值为2),lowbit(32) = 32(32的二进制表示为0b100000,0b100000的十进制值为32)......

lowbit的实现方法有两种:

1、消去最后一位的1然后再用原数字减去消去最后一位1的数。

int lowbit(int n){
    return n - (n & (n - 1));
}

/*
因为非负整数数n可以表示为x100...0,则n - 1可以表示为x011...1
其中x为n最后一位1前面的位,设最后一位1后面跟了m个0,则n - 1后最后一位1变为0,
后面的m个0变为1,两者做与运算后的值为x000...0,x后跟了m + 1个0,即最后一位1
被消掉了。
eg:0b1001101000 - 1 = 0b1001100111
   0b1001101000 & 0b1001100111 = 0b1001100000 最后一位1被消除了
所以 n & (n - 1)可以消除n的最后一位1
*/

2、将一个正整数与他的负数相与

int lowbit(int n){
    return n & (-n);
}

/*
对n的每一位取反再加1即可得到-n, 同样找到n的最后一位1,把最后一位1左边的位按位取反,
右边的位不变也可以求得-n,这是在学补码时学过的知识,所以n & (-n)可以获取lowbit值
*/

对于一个数组a,我们可以在其上建立一个这样的树状数组t:

该结构的性质:

  1. t[x]保存以x为根的子树中叶子结点的和。
  2. t[x]结点覆盖的长度等于lowbit(x),即t[x]表示的是从a[x] ~~ a[x - lowbit[x] + 1]之间所有值的和,包括端点,观察上图可知同一层的lowbit值都相同,所以同层的t[x]覆盖长度相同
  3. t[x]结点的父结点为t[x + lowbit(x)]
  4. 整棵树的高度为(logn) + 1

 对树状数组的操作:

1、add(index, k)操作,将数组a的下标为index的元素加上k

从子节点开始一层一层向上更改沿途的结点值。

void add(int index, int k){
    for(; index <= n; index += lowbit(index))t[index] += k;//n为根节点值
}

2、ask(int x)操作,返回下标x即前所有元素的和(前缀和)

 查询一个点的前缀和,需要从该点向左上不停的找上一个结点相加,而该结点减去lowbit恰好等于左上相邻结点。

int ask(int x){
    int ans = 0;
    for(; x != 0; x -= lowbit(x))ans += t[x];
    return ans;
}

若要求区间和,可以求出前缀和并相减。

3、built_tree()树状数组的创建

//给定数组nums,创建树状数组tree,nums是下标从0开始的数组
void built_tree(){
    int n = nums.size();
    //注意确保在进行迭代操作前tree中的所有元素值均为0
    for(int i = 0; i < n; i++)add(i + 1, nums[i]);
}

图片来源:〔manim | 算法 | 数据结构〕 完全理解并深入应用树状数组 | 支持多种动态维护区间操作_哔哩哔哩_bilibili

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力攻坚操作系统

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值