线段树与树状数组

树状数组

优点:代码短,运行效率高(大部分情况下与线段树相比大约差10倍),支持修改(在线做法)
能用树状数组做的尽量别用线段树(杀鸡不用牛刀)
解决问题:动态快速求前缀和(O(logn))

  1. 给某个位置上的数加一个数(单点修改)
  2. 快速求前缀和(区间查询)

个人理解:
在某些情况下,需要实现对序列中进行区间增加(差分能做到),之后再进行查询(前缀和能做到),但是前缀和是离线的不支持修改,而差分又不能很快的求出区间和,因此引出了这种方法。

构造方法:

  1. 维护一个和原数组a[n]一样长的数组(注意这里下标是从1开始的)
  2. 一共有四层,根据每个下标二进制表示数字的末尾有几个0代表第几层
  3. 例如其中奇数位置的值都等于原数组c[奇数] = a[奇数],因为它们的末尾有0个0,代表这都是第0层
  4. c[x]其实表示的是下标为(x - 2^k,x]这个区间的和,其中kx的二进制末尾0的个数,2^k可以用lowbit(x) = x&-x表示

请添加图片描述

例题代码:
leetcode307. 区域和检索 - 数组可修改

class NumArray {
public:
    vector<int> tr;
    vector<int> num;
    int lowbit(int x)
    {
        return x&-x;
    }

    void add(int val,int idx)
    {
        for(int i = idx;i<tr.size();i+=lowbit(i))
        {
            tr[i] += val;
        }
    }

    int query(int idx)
    {
        int sum = 0;
        for(int i = idx;i>0;i-=lowbit(i))
        {
            sum += tr[i];
        }
        return sum;
    }


    NumArray(vector<int>& nums):tr(nums.size()+1),num(nums) {
        
        for(int i = 0;i<nums.size();i++)
        {
            add(nums[i],i+1);
        }
    }
    
    void update(int index, int val) {
        add(val - num[index],index+1);
        num[index] = val;
    }
    
    int sumRange(int left, int right) {
        return query(right + 1) - query(left);
    }
};

线段树

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值