Binary Indexed Tree (Fenwick Tree)

Binary Indexed Tree 空间复杂度O(N),查询时间复杂度O(lgN). 其中每个元素,存储的是数组中一段(注意起始元素看作1而非0)的和:

假设这个元素下标为i,找到i的最低位1,从最低位1开始的低部表示的是长度,去除最低位1剩下的部分加上1表示的是起始位置,例如:

8二进制表示为100

最低位1也是最高位,从1开始的低部也即100本身,因此长度为8.

去除1以后,剩下为0,加上1以后为1。所以sum[8]表示的是从第1个元素开始的8个元素的和.

又比如11的二进制表示为1011

最低位1,因此表示的段长度为1。

去掉1以后剩下部分为1010,因此起始元素为第11个元素。所以sum[11]表示的就是原数组的第11个元素。

求i的最低位1(LSB)可以使用如下位运算:

i & (-i)

求1..i之和,以11为例

sum[11] = sum[8] + sum[10] + sum[11] 也即 0..8之和 + 9, 10之和 + 11。我们可以从最低位开始相加:

    int sum = 0;
    while (i > 0)
        sum += A[i], i -= LSB(i);
    return sum;

更新i的值,需要更新所有受影响的和,从sum[i]开始,逐渐扩大i的范围

    while (i < SIZE) 

        A[i] += k, i += LSB(i);

比如元素5被更新了,5表示为2进制是101,

那么我们首先更新元素5本身,

然后,5所在的2个元素的小区间:5,6也需要被更新也即 5 + LSB(5) = 6 被更新。

然后,更新5所在的更大的区间,1..8,也即: 6 + LSB(6) = 8。注意,并不存在5..8这样一个单独的4元素区间。每个区间的后半部分可以用这个区间减去前半部分得到:sum 5..8 = sum[8] – sum[4]。

上述代码来自Wiki

最后,求任意区间i..j的值可以由sum[j] – sum[i]得到。

以LeetCode 307. Range Sum Query – Mutable为例,以下为源码:

class NumArray{
public:
    // Binary Index Tree (Fenwick Tree)
    NumArray(vector<int> nums) {
        _nums = vector<int>(nums.size(), 0);
        sums = vector<int>(nums.size() + 1, 0);
        len = sums.size();
        for(int i = 0; i < len - 1; i++){
            update(i, nums[i]);
        }
    }

    void update(int i, int val) {
        int d = val - _nums[i];
        _nums[i++] = val;
        while(i < len){
            sums[i] += d;
            i += LBS(i);
        }
    }

    // [i+1, j+1] inclusive, so it should be sum[j+1] - sum[i]
    int sumRange(int i, int j) {
        return sumRange(++j) - sumRange(i);
    }
private:
    vector<int> sums;
    vector<int> _nums;
    int len;
    inline int LBS(int &i){
        return i & (-i);
    }

    inline int sumRange(int i){
        int sum = 0;
        while(i){
            sum += sums[i];
            i -= LBS(i);
        }
        return sum;
    }
};

原文地址:https://www.cnblogs.com/k330/p/Binary_Indexed_Tree_aka_Fenwick_Tree.html

时间: 2024-10-08 18:43:39

Binary Indexed Tree (Fenwick Tree)的相关文章

树状数组(Binary Indexed Tree,BIT)

树状数组(Binary Indexed Tree) 前面几篇文章我们分享的都是关于区间求和问题的几种解决方案,同时也介绍了线段树这样的数据结构,我们从中可以体会到合理解决方案带来的便利,对于大部分区间问题,线段树都有其绝对的优势,今天这篇文章,我们就来欣赏由线段树变形的另外一个数据结构--树状数组,树状数组通常也用于解决区间求和.单点更新的问题,而且效率比线段树高一些(树状数组区间求和和单点更新的时间复杂度均为o(log n)),相对而言,线段树的应用范围可能更广泛一些.但不得不承认,树状数组确

SRM 627 D1L2GraphInversionsDFS查找指定长度的所有路径 Binary indexed tree (BIT)

题目:http://community.topcoder.com/stat?c=problem_statement&pm=13275&rd=16008 由于图中边数不多,选择DFS遍历全部路径,计算路径Inversions时使用了一个R[] 数组,能够在O(N)时间内得到路径Inversions,又由于该图所以路径条数为O(N^2),算法复杂度为O(N^3),对于N为1000的限制来说,复杂度较高,但实际測试中,最慢的測试用例费时700多ms,没有超时.若要减小复杂度,须要更高效的算法来计

POJ1990 MooFest 树状数组(Binary Indexed Tree,BIT)

N头牛排成一列,每头牛的听力是Vi,每头牛的位置Pi,任意两头牛i,j相互交流时两头牛都至少需要发出声音的大小为max(Vi,Vj) * |Pi-Pj|,求这N头牛两两交流总共发出的声音大小是多少.N,V,P都是1-20000的范围. 这题首先对Vi从小到大进行排序,排序过后就可以依次计算i,将所有比Vi小的牛到i之间的距离之和乘以Vi得到Ri,然后累加Ri就是最终结果.问题是Ri具体该如何求. 假设听力比Vi小的牛并且位置也比Pi小的牛的个数为Ci,并且这些距离之和为Si,听力比Vi小的所有牛

Hdu5921 Binary Indexed Tree

Hdu5921 Binary Indexed Tree 思路 计数问题,题目重点在于二进制下1的次数的统计,很多题解用了数位DP来辅助计算,定义g(i)表示i的二进制中1的个数, $ans = \sum_{i=1}^n \sum_{j=0}^{i-1} g(i,j) = 0.5\sum_{i=0}^n\sum_{j=0}^n[g(i)+g(j)-2g(lcp(i,j))] $ 即先计算每个位的贡献,再减去重复的地方. 先计算前者,每个数会出现n+1 次,所以结果乘以n+1 即可,对第i位,统计这

poj 2985 The k-th Largest Group 求第K大数 Treap, Binary Index Tree, Segment Tree

题目链接:点击打开链接 题意:有两种操作,合并集合,查询第K大集合的元素个数.(总操作次数为2*10^5) 解法: 1.Treap 2.树状数组 |-二分找第K大数 |-二进制思想,逼近第K大数 3.线段树 4.... Treap模板(静态数组) #include <math.h> #include <time.h> #include <stdio.h> #include <limits.h> #include <stdlib.h> const

LeetCode OJ - Symmetric Tree &amp;&amp; Same Tree

这两道题,大同小异. 我都是用BFS,在遍历的过程,判断结构是否相同/对称,值是否相同. 下面是AC代码: 1 /** 2 * Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). 3 * @param root 4 * @return 5 */ 6 public boolean isSymmetricRecursively(TreeNode root){ 7

B-tree/B+tree/B*tree [转]

(原文出处:http://blog.csdn.net/hbhhww/article/details/8206846) B~树 1.前言: 动态查找树主要有:二叉查找树(Binary Search Tree),平衡二叉查找树(Balanced Binary Search Tree),红黑树 (Red-Black Tree ),B-tree/B+-tree/ B*-tree (B~Tree).前三者是典型的二叉查找树结构,其查找的时间复杂度O(log2N)与 树的深度相关,那么降低树的深度自然对查找

[Leetcode][Tree][Same Tree]

非常简单的一道题 /** * Definition for binary tree * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: bool isSameTree(TreeNode *p, TreeNode *q) { if (p ==

[置顶]B-tree/B+tree/B*tree [转]

(原文出处:http://blog.csdn.net/hbhhww/article/details/8206846) B~树 1.前言: 动态查找树主要有:二叉查找树(Binary Search Tree),平衡二叉查找树(Balanced Binary Search Tree),红黑树 (Red-Black Tree ),B-tree/B+-tree/ B*-tree (B~Tree).前三者是典型的二叉查找树结构,其查找的时间复杂度O(log2N)与 树的深度相关,那么降低树的深度自然对查找