惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
人人都是产品经理
人人都是产品经理
月光博客
月光博客
V
V2EX
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Last Week in AI
Last Week in AI
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
V
Visual Studio Blog
宝玉的分享
宝玉的分享
雷峰网
雷峰网
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - Franky
博客园 - 聂微东
博客园 - 司徒正美
博客园 - 【当耐特】
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
大猫的无限游戏
大猫的无限游戏
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志

博客园 - 谢绝围观

LeetCode 910. Smallest Range II EAC 抓取CD为AAC文件 Lint Code 1365. Minimum Cycle Section LintCode 896. Prime Product 简明题解 UVa 1471 - Defense Lines Windows下安装配置MinGW GCC调试环境 详解LeetCode 137. Single Number II LeetCode 309. Best Time to Buy and Sell Stock with Cooldown LeetCode 84. Largest Rectangle in Histogram 修改注册表删除Windows资源管理器 “通过QQ发送” 右键菜单项 LeetCode 312. Burst Balloons LeetCode 287. Find the Duplicate Number LeetCode 10. Regular Expression Matching LeetCode 117. Populating Next Right Pointers in Each Node II 在Windows Azure VM下搭建SSTP VPN - 谢绝围观 利用.NET Code Contracts实现运行时验证 Log4net 配置实例 解决Windows Server 2012 下利用RRAS创建VPN断线的问题 - 谢绝围观 慎用静态类static class
Binary Indexed Tree (Fenwick Tree)
谢绝围观 · 2018-05-27 · via 博客园 - 谢绝围观

Binary Indexed Tree 主要是为了存储数组前缀或或后缀和,以便计算任意一段的和。其优势在于可以常数时间处理更新(如果不需要更新直接用一个数组存储所有前缀/后缀和即可)。
空间复杂度O(n). 其中每个元素,存储的是数组中一段(起始元素看作为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;
    }
};