树状数组的原理与应用

一、原理

简介:

「树状数组」是一种可以动态维护序列前缀和的数据结构,它的功能是:

        单点更新 update(i, v): 把序列 i 位置的数加上一个值 v
        区间查询 query(i): 查询序列 [1⋯i] 区间的区间和,即 i 位置的前缀和
        修改和查询的时间代价都是 O(logn),其中 nn 为需要维护前缀和的序列的长度。

结构:

 上图中黑色数组是原来的数组,用A代替,红色数组是我们的树状数组,用C代替,由图可知

  • 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[i]:

原数组的前缀和: 

前辈的智慧2^k=i&(-i);//i为树状数组索引 

主要代码:

//长度
int n;
//对应原数组和树状数组
int a[105],c[105]; 

//根据索引计算2^k
int lowbit(int x){
    return x&(-x);
}

//在i位置加上k
void updata(int i,int k){
    while(i <= n){
        c[i] += k;
        i += lowbit(i);
    }
}

//求A[1 - i]的和
int getsum(int i){
    int res = 0;
    while(i > 0){
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

二、应用

思路:维护树状数组C,用于倒序遍历原数组时记录小于当前元素值的元素个数。

tick:离散化,首先对题目中的数组nums去重,然后排序,得到数组a,数状数组根据a维护

class Solution {
    private int[] c;
    private int[] a;

    public List<Integer> countSmaller(int[] nums) {
        List<Integer> resultList = new ArrayList<Integer>(); 
        discretization(nums);
        init(nums.length + 5);
        for (int i = nums.length - 1; i >= 0; --i) {
            int id = getId(nums[i]);
            resultList.add(query(id - 1));
            update(id);
        }
        Collections.reverse(resultList);
        return resultList;
    }

    private void init(int length) {
        c = new int[length];
        Arrays.fill(c, 0);
    }

    private int lowBit(int x) {
        return x & (-x);
    }

    private void update(int pos) {
        while (pos < c.length) {
            c[pos] += 1;
            pos += lowBit(pos);
        }
    }

    private int query(int pos) {
        int ret = 0;
        while (pos > 0) {
            ret += c[pos];
            pos -= lowBit(pos);
        }

        return ret;
    }

    private void discretization(int[] nums) {
        Set<Integer> set = new HashSet<Integer>();
        for (int num : nums) {
            set.add(num);
        }
        int size = set.size();
        a = new int[size];
        int index = 0;
        for (int num : set) {
            a[index++] = num;
        }
        Arrays.sort(a);
    }

    private int getId(int x) {
        return Arrays.binarySearch(a, x) + 1;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值