zoukankan      html  css  js  c++  java
  • 树状数组

    树状数组定义:

    是一个查询和修改复杂度都为log(n)的数据结构。可以用于处理前缀和的问题,动态维护前缀和的工具

    区间修改和区间查询用树状数组会显得很麻烦 相对而言用线段树会更灵活。

    基本操作:求数列区间和,可以对数列单点进行操作。

    前置知识:

    差分数组

    lowbit()操作:返回非负整数x 在二进制表示下,第一个1和后面的0表示的数值(十进制的值)。

    int lowbit(int x)
    {
         return x&(-x);
    }
    /*
    -i 代表i的负数 计算机中负数使用对应的正数的补码来表示
    k表示i的二进制中末尾连续0的个数。 例如 : i=6(0110) 此时 k=1 -i=-6=(1001+1)=(1010) i&(-i)=(0010)=2=2^K k=1. C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i]; C[i]=A[i-lowbit(i)+1]+A[i-lowbit(i)+2]+......A[i];
    */

    树状数组思想:

    区间查询——》前缀和  ——》树结构维护(log2n)

    树状数组 t[x] 保存以x为根的子树中叶节值的和 。

    观察 t[x] 中每个x的二进制,每一层末尾零相同 ,零的个数即K对应覆盖的长度,覆盖长度就是lowbit(x)。

     t [x] 的父节点为 t【x+lowbit[x]】 树的深度为 log2n+1

     

    基本操作(单点修改 查询前缀和)

    void update(int i,int val)//单点更新
    {
        while(i<=n){
            t[i]+=val;
            i+=lowbit(i);//由叶子节点向上更新树状数组C,从左往右更新
        }
    }
    int ask(int x)//求区间[1,i]内所有元素的和 即求前缀和
    {
        int ans=0;
        while(x>0){
            ans+=t[x];//从右往左累加求和
            x-=lowbit(x);
        }
        return ans;
    }

       

    树状数组初始化

    memset(a, 0, sizeof a);
    memset(c, 0, sizeof c);

    cin>>n;

    for(int i = 1; i <= n; i++){

       cin>>a[i];

       updata(i,a[i]); //输入初值的时候,也相当于更新了值

     }

     树状数组的用法

    1.单点修改,单点查询    update(x,val) ;  ask(x) - ask(x-1);

    2.单点修改,区间查询     update(x,val) ;  ask(r) - ask(l-1);

    3.区间修改,单点查询  (差分数组 ) 

     用树状数组维护差分数组的前缀和,即原数列的每个元素,由于区间修改 ,产生的改变量。

     区间修改 [l,r]+d    update(l,d)   update(r+1,-d) 

     查询 a[x]                ans =a[x]+ask(X) 

    4. 区间修改,区间查询 

       求出原数列a[x]的前缀和  ans=a[r]-a[l]   原数列的前缀和也可以用差分数组来求 

     

     

     

     参考

  • 相关阅读:
    工厂模式之抽象工厂
    c#中的Task异步编程
    c# 异步编程
    工厂模式之工厂方法模式
    c# 多线程——入门学习
    代理模式
    工厂模式之简单工厂
    SOA面向服务架构
    [LeetCode]387. 字符串中的第一个唯一字符
    [LeetCode]7. 整数反转
  • 原文地址:https://www.cnblogs.com/young-children/p/11749619.html
Copyright © 2011-2022 走看看