zoukankan      html  css  js  c++  java
  • 【基础算法-树状数组】入门-C++

    @

    基本定义

    树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。

    如何理解树状数组

    树状数组,重点在于它是树状(这不废话吗)
    大家都知道二叉树吧,贴一张二叉树的图给大家理解一下(自己画的有点丑)
    在这里插入图片描述
    我们把它变形一下...

    在这里插入图片描述
    现在定义每一列的顶端结点C[]数组
    在这里插入图片描述
    ps.最后一个图不是我画的

    C[i]代表子树的叶子结点的权值之和, 这里以求和举例

    如图可以知道

    C[1]=A[1];

    C[2]=A[1]+A[2];

    C[3]=A[3];

    C[4]=A[1]+A[2]+A[3]+A[4];

    C[5]=A[5];

    C[6]=A[5]+A[6];

    C[7]=A[7];

    C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];
    这就是树状数组的基本组成。

    将C[]数组的结点序号转化为二进制

    1=(001) C[1]=A[1];

    2=(010) C[2]=A[1]+A[2];

    3=(011) C[3]=A[3];

    4=(100) C[4]=A[1]+A[2]+A[3]+A[4];

    5=(101) C[5]=A[5];

    6=(110) C[6]=A[5]+A[6];

    7=(111) C[7]=A[7];

    8=(1000) C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];

    对照式子可以发现 C[i]=A[i-2^ k+1]+A[i-2^k+2]+......A[i];
    (k为i的二进制中从最低位到高位连续零的长度)例如i=8时,k=3;

    可以自行带入验证;

    现在引入lowbit(x)

    lowbit(x) 其实就是取出x的最低位1 换言之 lowbit(x)=2^k k的含义与上面相同 理解一下

    主要操作

    添加元素
    单点修改
    单点查询
    区间修改
    区间查询

    前两个普通数组能够O(1)时间复杂度完成,后两个普通数组需要O(n)时间复杂度完成,而树状数组最大只需要O(logn),这也正是树状数组的快捷之处。

    代码实现

    0.lowbit操作

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

    不懂的看下上面引用的那一段
    1.添加元素

    void add(int s,int num)
    {
    	for(long long i=s;i<=n;i+=lowbit(i))
    		tree[i]+=num;
    	return;
    }
    

    添加元素的操作可能有些不好理解,但同上,只要理解了lowbit操作,基本就能看懂这个添加操作了...
    2.单点修改
    这个操作普通数组只要O(1)的时间复杂度,但是树状数组需要最高 O(logn)的时间,因为在树状数组中数组中的一些元素是有联系的,修改其中一个就需要牵扯到很多...

    void add(int x,int k)
    {
        while(x<=n)
        {
            tree[x]+=k;
            x+=lowbit(x);
        }
    }
    

    3.单点查询
    依然,普通数组O(1),树状数组最高O(logn)

    long long ask(long long s)
    {
        long long ans=0;
        for(long long i=s;i>=1;i-=lowbit(i))
    		ans+=tree[i];
        return ans;
    }
    

    4.区间修改
    这个操作就是树状数组的强项之一了,普通数组O(n),树状数组也是不到O(logn)跑出来

    void add(int s,int num)
    {
    	for(long long i=s;i<=n;i+=lowbit(i))
    		tree[i]+=num;
    }
    

    p.s.把[x,y]区间的数加上s,需要add(x,s);add(y+1,-s);
    5.区间查询
    这依然是树状数组的强项,时间复杂度同4

    int sum(int x)
    {
        int ans=0;
        while(x!=0)
        {
            ans+=tree[x];
            x-=lowbit(x);
        }
        return ans;
    }
    
    

    树状数组模板题:
    只需要树状数组基本知识
    需要用到差分
    ov.

    个人博客地址: www.moyujiang.com 或 moyujiang.top
  • 相关阅读:
    转:MVC分页
    转:更新Android SDK之后Eclipse提示ADT版本过低的一个简易解决办法
    DataGridView 添加鼠标右键选择行
    WinForm 中使用ScintillaNet
    C#获取当前程序运行路径的方法集合
    Winform DataGridView鼠标右键选择列
    EF 数据查询(更改默认排序)
    使用lambda表达式进行对象结合的筛选操作
    使用SSIS生成数据导出为Excel文件的模板
    Sql server 数据库中计算每天的开始结束时间
  • 原文地址:https://www.cnblogs.com/moyujiang/p/11215596.html
Copyright © 2011-2022 走看看