树状数组Binary Indexed Trees详解与Java实现

WiKi

树状数组是由Peter Fenwick在1994年提出的,所以又称为Fenwick Tree。数组的区间求和的复杂度是O(n),树状数组可以将数组区间求和的复杂度降低到O(lg n)。这对于长数组的高频率区间求和的应用场景来讲,可以提高效率。

参考

树状数组(Binary Indexed Trees)
搞懂树状数组

详解

这里从上面的参考中总结我的思路。
树状数组通过树形结构对原始数组进行预处理,树的每个节点存储了原始数组的某些区间和,这样,原始数组的区间和就可以通过树形结构将算法复杂度降低到O(lg n)。
例如,下图a数组是原始数组,而c数组就是对a数组进行预处理后的树行结构。
这里写图片描述

从a数组如何获得c数组呢?这里要借助lowBit(int K)函数。lowBit(int K)函数保留k的二进制最低位1的值。例如,1110保留最低位1即0010。

private static int lowBit(int k){
        return k&-k;
    }

从a数组和c数组满足下面的累加公式。即,c[i]等于从a数组从i开始向左累加lowBit(i)个值。
这里写图片描述
例如:
c[1]等于a数组从1开始向左累加lowBit(1)=1个,即c[1]=a[1];
c[2]等于a数组从2开始向左累加lowBit(2)=2个,即c[2]=a[2]+a[1];
……
c[7]等于a数组从2开始向左累加lowBit(7)=1个,即c[2]=a[7];
c[8]等于a数组从2开始向左累加lowBit(8)=8个,即c[2]=a[8]+a[7]+…a[1];
那么,构造好了c数组,求和就变得简单了。
这里写图片描述
直到求和下标为1即累加到了a[1]。
这里写图片描述
代码实现:

/**
     * 计算1~index范围内和
     * index一直减去lowBit(index),直到index为0
     * */
    public int sum(int index){
        if (index<1&&index>length) {
            throw new IllegalArgumentException("Out of Range!");
        }
        int sum=0;
        while (index>0) {
            sum+=tree[index];
            index-=lowBit(index);
        }
        return sum;
    }

到这里,求和的方式已经介绍完毕。
两点注意:
1、实际上并没有一个原始数组a一直存在,否则就多耗费了一倍的存储空间。
2、数组一般从1开始算有效位,0位置为无效位,这样不容易混淆。

Java实现

这里实现了BinaryIndexedTree的类。

/**
 * 树状数组的Java版本,created by 曹艳丰  2016.07.09
 * 原理参考:http://www.hawstein.com/posts/binary-indexed-trees.html
 * 或:https://www.topcoder.com/community/data-science/data-science-tutorials/binary-indexed-trees/
 * */
public class BinaryIndexedTree {
    public int length;
    private int[] tree;
    /**
     * 为了统一下标,所以tree[0]不被使用,数组有效范围1~length。
     * */
    public BinaryIndexedTree(int length){
        this.length=length;
        tree=new int[length+1];
    }
    /**
     * 计算1~index范围内和
     * index一直减去lowBit(index),直到index为0
     * */
    public int sum(int index){
        if (index<1&&index>length) {
            throw new IllegalArgumentException("Out of Range!");
        }
        int sum=0;
        while (index>0) {
            sum+=tree[index];
            index-=lowBit(index);
        }
        return sum;
    }
    /**
     * 计算start~end范围内和
     * */
    public int  sum(int start,int end) {
        return sum(end)-sum(start-1);
    }
    /**
     * index一直加上lowBit(index),直到index为length。这些位置的值都加上value
     * */
    public void put(int index,int value){
        if (index<1&&index>length) {
            throw new IllegalArgumentException("Out of Range!");
        }
        while (index<=length) {
            tree[index]+=value;
            index+=lowBit(index);
        }
    }
    /**
     * index一直减去lowBit(index),直到index为length。这些位置的值都加上value
     * */
    public int get(int index){
        if (index<1&&index>length) {
            throw new IllegalArgumentException("Out of Range!");
        }
        int sum=tree[index];
        int z=index-lowBit(index);
        index--;
        while (index!=z) {
            sum-=tree[index];
            index-=lowBit(index);
        }
        return sum;
    }
    /**
     * 保留k的二进制最低位1的值。例如,1110保留最低位1即0010.
     * */
    private static int lowBit(int k){
        return k&-k;
    }
}

并进行了测试

import java.util.Random;

public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int length=15;
        BinaryIndexedTree bTree=new BinaryIndexedTree(length);
        Random random=new Random();
        //随机放满数据
        for (int i = 1; i <= bTree.length; i++) {
            bTree.put(i, random.nextInt(100));
        }
        //取出每一位
        for (int i = 1; i <= bTree.length; i++) {
            int value=bTree.get(i);
            System.out.printf("%3d",value);
            System.out.print("  ");
        }
        System.out.println();
        //计算0~i的和
        for (int i = 1; i <= bTree.length; i++) {
            int sum=bTree.sum(i);

            System.out.printf("%3d",sum);
            System.out.print("  ");
        }
        System.out.println();
        //计算start~end的和
        System.out.printf("%3d",bTree.sum(2,4));
    }

}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是一个简单的 Java 实现二维数组二分查找的示例代码: ```java public class BinarySearch2D { public static boolean search(int[][] matrix, int target) { int m = matrix.length; if (m == 0) { return false; } int n = matrix[0].length; int left = 0, right = m * n - 1; while (left <= right) { int mid = (left + right) / 2; int midValue = matrix[mid / n][mid % n]; if (midValue == target) { return true; } else if (midValue < target) { left = mid + 1; } else { right = mid - 1; } } return false; } } ``` 其中,`search` 方法接收一个二维数组 `matrix` 和一个目标值 `target`,返回一个布尔值表示目标值是否存在于数组中。 首先,我们获取数组的行数 `m` 和列数 `n`。然后,我们使用二分查找的思想来查找目标值。我们定义一个 `left` 变量表示左边界,初始值为 0;定义一个 `right` 变量表示右边界,初始值为 `m * n - 1`,即数组中最后一个元素的索引。在每次循环中,我们计算中间元素的索引 `mid`,并获取中间元素的值 `midValue`。如果 `midValue` 等于 `target`,说明目标值存在于数组中,返回 `true`。如果 `midValue` 小于 `target`,说明目标值在中间元素的右边,更新左边界为 `mid + 1`。如果 `midValue` 大于 `target`,说明目标值在中间元素的左边,更新右边界为 `mid - 1`。如果最终未找到目标值,返回 `false`。 使用示例: ```java int[][] matrix = { {1, 3, 5, 7}, {10, 11, 16, 20}, {23, 30, 34, 50} }; int target = 3; boolean result = BinarySearch2D.search(matrix, target); System.out.println(result); // true ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值