0 引言

Leetcode307

这道题给一个可变数组,求从\(i\)到\(j\)的元素之和。

一个naive的做法是,每次查询都从\(i\)累加到\(j\):

class NumArray {
public:
NumArray(vector<int>& nums) {
nums_ = nums;
} void update(int i, int val) {
nums_[i] = val;
} int sumRange(int i, int j) {
int ans = 0;
for(int l = i;l <= j;++l)
ans += nums_[l];
return ans;
}
private:
vector<int> nums_;
};

这种方法每次更新的复杂度为\(O(1)\),每次查询的复杂度为\(O(n)\)。

1 树状数组

为了降低查询的复杂度,引入Binary Index Tree(Fenwick Tree):

BIT其实并不是树,而是维护了一个前缀和数组prefixSums_

假设有一个数组:



那么我们的tree:



0是dummy node,将结点的二进制表示的最后一个1翻转,就能得到其父结点。

下来填充这棵树:

\(1=0+2^0\),存储从下标0开始的前1个数的和:3(0,0);

\(2=0+2^1\),存储从下标0开始的前2个数的和:5(0,1);

\(3=2^1+2^0\),存储从下标2开始的前1个数的和:-1(2,2);

\(4=0+2^2\),存储从下标0开始的前4个数的和:10(0,3);

\(5=2^2+2^0\),存储从下标4开始的前1个数的和:5(4,4);

\(6=2^2+2^1\),存储从下标4开始的前2个数的和:9(4,5);

\(7=2^2+2^1+2^0\),存储从下标6开始的前1个数的和:-3(6,6);

\(8=0+2^3\),存储从下标0开始的前8个数的和:19(0,7);

\(9=2^3+2^0\),存储从下标8开始的前1个数的和:7(8,8);

\(10=2^3+2^1\),存储从下标8开始的前2个数的和:9(8,9);

\(11=2^3+2^1+2^0\),存储从下标10开始的前1个数的和:3(10,10);

填充后的tree:



接下来就可以根据这棵树来计算prefixSums_

假如要计算\(0-5\)的和,从下标6出发,一直加到dummy node,得到prefixSums_[6]=9+10=19

要计算\(0-9\)的和,从下标10出发,一直加到dummy node,得到prefixSums_[10]=9+19=28

以计算\(0-9\)的和为例,结点10存储的是(8,9)的部分和,结点8存储的是(0,7)的部分和,所以加起来就是\(0-9\)的和。

2 快速实现

上面求结点的父结点、将下标拆解为二进制去填充树的方式很慢,来看一种稍快的方式。

查询时,我们需要计算从某结点到dummy node的和,这就涉及计算该结点的parent:

假如要求结点7的parent,7的二进制原码为111,-7的补码为001,将原码和补码按位与得001,用原码减去001,得110=6,即7的父结点是6。

更新时,我们需要更新所有包含该结点的部分和结点:

假如更新了结点1,1的二进制原码为001,-1的补码为111,将原码和补码按位与得001,用原码加上001,得010=2,即还要更新结点2,更新了结点2,还要更新结点4......

最后来看下非常简洁的实现:

class BIT{
private:
vector<int> prefixSums_;
static inline int lowbit(int x) {
return x & (-x);
}
public:
BIT(int n) : prefixSums_(n + 1, 0) {} void update(int i, int delta) {
while(i < prefixSums_.size()) {
prefixSums_[i] += delta;
i += lowbit(i);
}
} int query(int i) {
int sum = 0;
while(i > 0) {
sum += prefixSums_[i];
i -= lowbit(i);
}
return sum;
}
};

BIT每次查询以及更新的复杂度都是\(O(lgn)\),适用于动态的更新以及实时查询。

3 Reference

Fenwick Tree or Binary Indexed Tree

花花酱 Fenwick Tree / Binary Indexed Tree SP3

Binary Index Tree的更多相关文章

  1. 树状数组(binary index tree)

    概述 修改和查询复杂度为log(n)的数据结构,所有奇数位的数和原数位置相同,偶数位置是原数组若干位置的和. 假如原数组A(a1, a2, a3, a4 ...),和其对应的树状数组C(c1, c2, ...

  2. 树状数组(Binary Index Tree)

    一维BIT(单点更新,区间求和): Problem - 1166 #include <iostream> #include <algorithm> #include <c ...

  3. Leetcode: Range Sum Query 2D - Mutable && Summary: Binary Indexed Tree

    Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...

  4. Binary Indexed Tree (Fenwick Tree)

    Binary Indexed Tree 主要是为了存储数组前缀或或后缀和,以便计算任意一段的和.其优势在于可以常数时间处理更新(如果不需要更新直接用一个数组存储所有前缀/后缀和即可).空间复杂度O(n ...

  5. Leetcode: Convert sorted list to binary search tree (No. 109)

    Sept. 22, 2015 学一道算法题, 经常回顾一下. 第二次重温, 决定增加一些图片, 帮助自己记忆. 在网上找他人的资料, 不如自己动手. 把从底向上树的算法搞通俗一些. 先做一个例子: 9 ...

  6. [LeetCode] Convert Sorted List to Binary Search Tree 将有序链表转为二叉搜索树

    Given a singly linked list where elements are sorted in ascending order, convert it to a height bala ...

  7. LeetCode Verify Preorder Sequence in Binary Search Tree

    原题链接在这里:https://leetcode.com/problems/verify-preorder-sequence-in-binary-search-tree/ 题目: Given an a ...

  8. 【leetcode】Binary Search Tree Iterator

    Binary Search Tree Iterator Implement an iterator over a binary search tree (BST). Your iterator wil ...

  9. leetcode98 Validate Binary Search Tree

    题目: Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is define ...

随机推荐

  1. 家庭记账本app进度之关于tap的相关操作1

    今天还主要学习关于怎样制作微信的先关的tap. 今天的主要成果是已经了解了相关的技术,以及相关的思路.代码经过一个下午的编写,基本接近尾声. 更详细的实验代码,以及相关的知识点将在明天完善后进行发表. ...

  2. Linux Shell编程,双括号运算符(())

    双括号运算符是shell非常强大的扩展. 这里简要介绍两种使用方式: 1.条件判断 跟在if.while.until,for等需要逻辑条件的命令后,进行逻辑判断 if(( expr));then … ...

  3. Web Scraper 高级用法——使用 CouchDB 存储数据 | 简易数据分析 18

    这是简易数据分析系列的第 18 篇文章. 利用 web scraper 抓取数据的时候,大家一定会遇到一个问题:数据是乱序的.在之前的教程里,我建议大家利用 Excel 等工具对数据二次加工排序,但还 ...

  4. wireshark没有找到接口

    今天安装wireshark,打开发现显示没有找到接口,网上搜索发现出现这种问题的都是win10,但是我的是win7 看了一下win10这种问题的原因是自带的winpcap不支持win10,应到http ...

  5. SolrCloud(solr集群+zookeeper集群)

    一.集群介绍 1.  什么是SolrCloud SolrCloud(solr 云)是Solr提供的分布式搜索方案,当你需要大规模,容错,分布式索引和检索能力时使用 SolrCloud.当一个系统的索引 ...

  6. AJ学IOS 之小知识iOS启动动画_Launch Screen的运用

    AJ 分享,必须精品 看下效果吧 例如新浪微博的软件开启时候 就是这个 用Launch image实现 这个不难,就是在Images.xcassets 增加一个LaunchImage文件(右键 new ...

  7. c++ string类的一些使用

    初始化: string类的初始化是不可以用字符进行的,如; string str='c'; string str('c');必须传递字符串字面量作为参数:string本身是用模板类进行实例化的类. s ...

  8. PHP代码审计理解(三)---EMLOG某插件文件写入

    此漏洞存在于emlog下的某个插件---友言社会化评论1.3. 我们可以看到, uyan.php 文件在判断权限之前就可以接收uid参数.并且uid未被安全过滤即写入到了$uyan_code中. 我们 ...

  9. [PHP]$_SERVER参数详情

    来源:PHP中$_SERVER的详细参数与说明 $_SERVER['PHP_SELF'] #当前正在执行脚本的文件名,与 document root相关.$_SERVER['argv'] #传递给该脚 ...

  10. Mybatis Generator通用Join的实现

    通常,我们使用Mybatis实现join表关联的时候,一般都是通过在xml或注解里写自定义sql实现. 本文通过Mybatis Generator的插件功能新增一个JoinPlugin插件,只要在配置 ...