树状数组

树状数组

1.什么是树状数组?

顾名思义,就是用数组来模拟树形结构呗。那么衍生出一个问题,为什么不直接建树?答案是没必要,因为树状数组能处理的问题就没必要建树。和Trie树的构造方式有类似之处。

2.树状数组可以解决什么问题

可以解决大部分基于区间上的更新以及求和问题。

3.树状数组和线段树的区别在哪里

树状数组可以解决的问题都可以用线段树解决,这两者的区别在哪里呢?树状数组的系数要少很多,就比如字符串模拟大数可以解决大数问题,也可以解决1+1的问题,但没人会在1+1的问题上用大数模拟。

4.树状数组的优点和缺点

修改和查询的复杂度都是O(logN),而且相比线段树系数要少很多,比传统数组要快,而且容易写。

缺点是遇到复杂的区间问题还是不能解决,功能还是有限。

树状数组基本模板

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

int lowbit(int x){//得到最低位0的位置
    return x&(-x);
}

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

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

区间更新时涉及到差分数组的运用

假设我们规定A[0] = 0;

则有 A[i] = Σij = 1D[j];(D[j] = A[j] - A[j-1]),即前面i项的差值和,这个有什么用呢?例如对于下面这个数组

  • A[] = 1 2 3 5 6 9
  • D[] = 1 1 1 2 1 3

如果我们把[2,5]区间内值加上2,则变成了

  • A[] = 1 4 5 7 8 9
  • D[] = 1 3 1 2 1 1

当某个区间[x,y]值改变了,区间内的差值是不变的,只有D[x]和D[y+1]的值发生改变

树状数组求逆序数:

class Solution {
    private int[] c;
    public List<Integer> countSmaller(int[] nums) {
        List<Integer> v = new ArrayList<Integer>();
        List<Integer> resultList = new ArrayList<Integer>();
        c = new int[nums.length+5];
        Arrays.fill(c, 0);
        //去重
        for(int i=0;i<nums.length;i++){
            if (!v.contains(nums[i])) {
        		v.add(nums[i]);
        	}
        }
        // int[] m=new int[nums.length];
        //离散化,先排序,然后二分查找出对应元素的等效值
        Collections.sort(v);
        int[]m=new int[v.size()];
        for(int i=0;i<v.size();i++){
            m[i]=v.get(i);
        }
        //从后往前遍历整个序列,将出现过的元素在树状数组中记录+1,添加之前,比当前元素小的前缀和就是当前位置的逆序数
        for(int i=nums.length-1;i>=0;i--){
            int id = Arrays.binarySearch(m, nums[i]) + 1;
            resultList.add(getsum(id - 1));
            update(id,1);
        }
        Collections.reverse(resultList);
        return resultList;
    }
    public int lowbit(int x){
        return x&(-x);
    }
    //在i的位置加上k
    public void update(int i,int k){
        while(i < c.length){
            c[i] += k;
            i += lowbit(i);
        }
    }
    //获取从1到i的和
    public int getsum(int i){
        int res = 0;
        while(i > 0){
            res += c[i];
            i -= lowbit(i);
        }
        return res;
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值