数据结构——树状数组(区间更新、求和问题)

假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说:
实现 track(int x) 方法,每读入一个数字都会调用该方法;
实现 getRankOfNumber(int x) 方法,返回小于或等于 x 的值的个数。
leetcode

解题思路:这是一道树状数组地模版题。

什么是树状数组呢?

就是将数组元素信息整合在一些关键节点上,这样,需要获取某些信息的时候,就不需要去遍历每一个节点,只需要去检索这些关键的节点,就能把时间复杂度降到O(logn)。

如何构建树状数组

在这里插入图片描述
设C即为红色节点,每个红色节点的值就是其子节点的总和。树状数组中,节点值的表达式为

C [ i ] = A [ i − 2 k + 1 ] + A [ i − 2 k + 2 ] + . . . + A [ i ] ; C[i] = A[i - 2^k+1] + A[i - 2^k+2] + ... + A[i]; C[i]=A[i2k+1]+A[i2k+2]+...+A[i];

k为i的二进制中从最低位到高位连续零的长度。

其中,如何求2的k次方呢?这个方法叫做lowbit 利用计算机补码和位与的特性:x & -x;

求和问题

这里需要快速求某个黑色节点一下的和。通过树状数组的思想。这里的遍历步长就是lowbit。

在添加节点的时候,就遍历他的父节点,把他们的值都加一。

在获取值的时候,累加小于等于其值的关键节点。

为什么每次从x + 1开始枚举呢?
0位不用啊,因为0的lowbit还是0,这样就进入死循环了。

class StreamRank {
    int[] nums;
    int n = 50010;
    public StreamRank() {
        nums = new int[50010];
    }
    
    public void track(int x) {
        for(int i = x + 1; i <= n; i += lowbit(i)) {
            nums[i] += 1;
        }
    }
    
    public int getRankOfNumber(int x) {
        int res = 0;

        for(int i = x + 1; i != 0; i -= lowbit(i)) {
            res += nums[i];
        }

        return res;
    }

    int lowbit(int x) {
    	// 获取x的最低位值 奇数就是1 偶数就是能整除这个数的最大的2的幂 0就为0
        return x & -x;
    }
}

posted @ 2020-11-12 15:07  lippon  阅读(136)  评论(0编辑  收藏  举报