Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/google-apps-script/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 时间复杂度-二叉索引树_Java_Algorithm - Fatal编程技术网

Java 时间复杂度-二叉索引树

Java 时间复杂度-二叉索引树,java,algorithm,Java,Algorithm,我正在写一个二叉索引树。作为文档,它需要nlogn时间进行预处理。但我不明白为什么 在我的例子中,我从数组构造树,这应该需要2n次,因为第一次遍历数组一次,使其成为二叉树,然后更新sum,我再次以后序方式遍历树。所以总2n,而不是nlogn 有人能解释为什么需要nlogn时间来预处理二叉索引树吗 public class BITree { private class BTN { int data; int index; BTN left,right; publ

我正在写一个二叉索引树。作为文档,它需要nlogn时间进行预处理。但我不明白为什么

在我的例子中,我从数组构造树,这应该需要2n次,因为第一次遍历数组一次,使其成为二叉树,然后更新sum,我再次以后序方式遍历树。所以总2n,而不是nlogn

有人能解释为什么需要nlogn时间来预处理二叉索引树吗

public class BITree {

private class BTN {
    int data;
    int index;
    BTN left,right;

    public BTN(int data) {
        this.data = data;
    }
}
BTN head = null;


public BTN toBT(int[] arr,int start,int end){
    if(start <= end){
        int mid = start + (end - start)/2;
        BTN btn = new BTN(arr[mid]);
        btn.index = mid+1;
        btn.left = toBT(arr,start,mid-1);
        btn.right = toBT(arr,mid+1,end);
        return btn;
    }
    return null;
}

public  int sumAtIndex(BTN btn,int index){
    int sum = 0;
    if(index < btn.index)
        sum += sumAtIndex(btn.left,index);
    else if(index > btn.index) {
        sum += btn.data + sumAtIndex(btn.right, index);
    }
    if(btn.index == index)
        return btn.data + sum;
    return sum;
}

public int replaceSum(BTN btn){
    if(btn == null){
        return  0;
    }
    int l = replaceSum(btn.left);
    int r = replaceSum(btn.right);
    int sum = btn.data + l + r;
    btn.data += l;
    return sum;
}

void inOrder(BTN btn){
    if(btn != null) {
        inOrder(btn.left);
        System.out.print((btn.index+":"+btn.data)+",");
        inOrder(btn.right);
    }
}

public static void main(String[] args) {
    int[] arr = {5,1,6,4,2,3,3};
    BITree s2 = new BITree();
    BTN btn = s2.toBT(arr,0,arr.length-1);
    s2.replaceSum(btn);
    s2.inOrder(btn);
    System.out.println();
    System.out.println(s2.sumAtIndex(btn,3));
}


}
公共类比特树{
专用类BTN{
int数据;
整数指数;
BTN左,右;
公共BTN(int数据){
这个数据=数据;
}
}
BTN头=空;
公共BTN toBT(int[]arr,int start,int end){
如果(启动btn.index){
总和+=btn.data+sumAtIndex(btn.right,索引);
}
如果(btn.index==索引)
返回btn.data+总和;
回报金额;
}
公共整数替换和(BTN BTN){
如果(btn==null){
返回0;
}
int l=替换和(btn.左);
int r=替换和(btn.右侧);
整数和=btn.data+l+r;
btn.data+=l;
回报金额;
}
无效顺序(BTN BTN){
如果(btn!=null){
顺序(btn.左);
系统输出打印((btn.index+“:”+btn.data)+“,”);
顺序(btn.右);
}
}
公共静态void main(字符串[]args){
int[]arr={5,1,6,4,2,3,3};
比特树s2=新的比特树();
BTN BTN=s2.toBT(arr,0,arr.length-1);
s2.替换总数(btn);
s2.有序(btn);
System.out.println();
System.out.println(s2.sumAtIndex(btn,3));
}
}

此问题与以下问题重复:

@Thilo,感谢您指出了位预处理的优化方法。这可以在O(n)时间内完成

@SanketMakani,感谢分享链接,它很好地解释了这一点

这是工作代码,带有O(n)预处理时间

package com.rabin;

import java.util.StringJoiner;

/**
 * 
 */
public class BITree {
    /**
     * O(logn)
     * @param arr
     * @param index
     * @param val
     */
    void update(int arr[],int index, int val)
    {
        index++;
        for(; index <= arr.length-1; index += index&-index)
            arr[index] += val;
    }

    /**
     * O(logn)
     * @param arr
     * @param noOfElements
     * @return
     */
    int query(int[] arr,int noOfElements)
    {
        int sum = 0;
        for(; noOfElements > 0; noOfElements -= noOfElements&-noOfElements)
            sum += arr[noOfElements-1];
        return sum;
    }

    /**
     * O(n)
     * @param arr
     */
    void toBIT(int[] arr){
        int n = arr.length;
        for(int i=1;i<=n;i++){
            int j = i+ (i & -i);
            if(j <= n)
                arr[j-1] += arr[i-1];
        }
    }

    static String arrayToString(int[] arr){
        StringJoiner sj = new StringJoiner(",","[","]");
        for(int i = 0; i< arr.length ;i++){
            sj.add(String.valueOf(arr[i]));
        }
        return sj.toString();
    }

    public static void main(String[] args) {
        int[] arr = {5,1,6,4,2,3,3};
        BITree bit = new BITree();

        System.out.println("Original Array:" +arrayToString(arr));
        bit.toBIT(arr);
        System.out.println("BIT Array:" +arrayToString(arr));
        System.out.println("Sum of first 5 nos : "+ bit.query(arr,5));
        bit.update(arr,0,8);
        System.out.println("Sum of first 5 nos after update : "+ bit.query(arr,5));

    }
}
package com.rabin;
导入java.util.StringJoiner;
/**
* 
*/
公共类比特树{
/**
*O(后勤)
*@param-arr
*@param索引
*@param val
*/
无效更新(int arr[],int index,int val)
{
索引++;
对于(;索引0;noOfElements-=noOfElements和-noOfElements)
总和+=arr[noOfElements-1];
回报金额;
}
/**
*O(n)
*@param-arr
*/
无效托比特(整数[]arr){
int n=阵列长度;

对于(int i=1;i@RBanerjee编写得很好的代码,最好使用一个额外的索引来实现位,这有助于代码理解。此外,它还表示一件额外的事情-位索引中最低有效的1位表示特定索引存储了多少个元素。例如,index=2(010)可以表示位中的索引2保存2个元素的值,类似地,4(100)表示4,6(110)存储2个值(即索引5和6),依此类推

另外,在你的更新方法中,你不是在更新值本身。你是在添加给定的值。我认为这并不意味着更新的意义。这是一个非常主观的讨论,但我认为它是一个更新,而不是一个增量。因此,如果索引5最初包含值2,当我想将其更新为-1时,它意味着val在索引5处更新之后的ue是-1而不是1

作为额外步骤,最好提供一种查询数组中范围的方法。例如,索引2和5(包括)之间的值是多少


包数据结构实现;
导入java.util.StringJoiner;
公共类二进制索引树{
私有final int[]位;
私人最终整数[]nums;
私人终审法院;
公共二进制索引树(int[]nums){
n=单位长度;
位=新整数[n+1];
this.nums=nums;
System.arraycopy(nums,0,bit,1,nums.length);
build();
}
/**
*在O(n)时间内构建二叉索引树。
*/
私有void build(){
int j;

对于(inti=1;i),这不是一个
二叉索引树(Fenwick树)
。这是一个简单的
BST
,经过一些修改。对于
二叉索引树
,请看一看。我是按照这个做的。@RBanerjee当我学习这个的时候,我引用了这个。它写得非常好,肯定会消除你的疑问,它与简单的
二叉树
@SanketMa有什么不同卡尼查看维基百科上的Talk标签:@Thilo,非常感谢。你消除了我的疑虑,我学到了一个有趣的方法。:)写得很好的代码。虽然我用从
1到n的索引来实现
n
元素,这消除了在为
update
query
编写代码时出错的可能性。对于索引
1
n
,您不需要从I中减去
-1
你得到的ndex。
<!-- language: java -->


package DataStructureImplementation;

import java.util.StringJoiner;

public class BinaryIndexedTree {

    private final int[] bit;
    private final int[] nums;
    private final int n;

    public BinaryIndexedTree(int[] nums) {
        n = nums.length;
        bit = new int[n + 1];
        this.nums = nums;
        System.arraycopy(nums, 0, bit, 1, nums.length);
        build();
    }

    /**
     * Builds a binary indexed tree in O(n) time.
     */
    private void build() {
        int j;
        for (int i = 1; i <= n; ++i) {
            j = i + (i & -i);
            if (j <= n) bit[j] += bit[i];
        }
    }

    /**
     * Updates an indexed item in the original array to the given value.
     * Also updates the values in the 'BIT' in O(logn) time.
     * @param index - index of the item to update
     * @param value - value to update to
     */
    public void update(int index, int value) {
        int diff = value - nums[index];
        nums[index] = value;
        index++;

        while (index <= n) {
            bit[index] += diff;
            index += (index & -index);
        }
    }

    /**
     * Queries the sum of the first 'K' indices in the original array in O(logn) time.
     * @param k - the number of items to aggregate.
     * @return - the sum of first 'K' numbers in the original array.
     * @throws Exception - if 'K' is out of bounds.
     */
    public int query(int k) throws Exception {
        if (k < 0 || k > n) throw new Exception("Invalid query range : " + k);
        int sum = 0;

        while (k > 0) {
            sum += bit[k];
            k -= (k & -k);
        }

        return sum;
    }

    /**
     * Queries the sum of numbers from the original array between index1 and index2 (inclusive) in O(logn) time.
     * @param index1 - left index.
     * @param index2 - right index.
     * @return - the sum of numbers between the given ranges.
     * @throws Exception - if range is out of bounds.
     */
    public int queryRange(int index1, int index2) throws Exception {
        return query(index2 + 1) - query(index1);
    }

    /**
     * Helper method to print the array contents.
     * @param nums - the array to print.
     * @return - the contents of the array as string.
     */
    static String arrayToString(int[] nums){
        StringJoiner stringJoiner = new StringJoiner(",","[","]");

        for (int n : nums) {
            stringJoiner.add(String.valueOf(n));
        }

        return stringJoiner.toString();
    }

    public static void main(String[] args) throws Exception {
        int[] nums = {5,8,5,4,2,3};

        BinaryIndexedTree binaryIndexedTree = new BinaryIndexedTree(nums);
        System.out.println("Original Array : " + arrayToString(nums));
        System.out.println("BIT Array : " + arrayToString(binaryIndexedTree.bit));
        System.out.println("Sum of first 5 nos : " + binaryIndexedTree.query(5));
        binaryIndexedTree.update(4,-1);
        System.out.println("Original Array after update : " + arrayToString(nums));
        System.out.println("BIT Array after update : " + arrayToString(binaryIndexedTree.bit));
        System.out.println("Sum of first 5 nos after update : " + binaryIndexedTree.query(5));
        System.out.println("Sum of numbers in range 2-5 : " + binaryIndexedTree.queryRange(2, 5));
    }
}