Binary Index Tree
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的更多相关文章
- 树状数组(binary index tree)
概述 修改和查询复杂度为log(n)的数据结构,所有奇数位的数和原数位置相同,偶数位置是原数组若干位置的和. 假如原数组A(a1, a2, a3, a4 ...),和其对应的树状数组C(c1, c2, ...
- 树状数组(Binary Index Tree)
一维BIT(单点更新,区间求和): Problem - 1166 #include <iostream> #include <algorithm> #include <c ...
- 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 ...
- Binary Indexed Tree (Fenwick Tree)
Binary Indexed Tree 主要是为了存储数组前缀或或后缀和,以便计算任意一段的和.其优势在于可以常数时间处理更新(如果不需要更新直接用一个数组存储所有前缀/后缀和即可).空间复杂度O(n ...
- Leetcode: Convert sorted list to binary search tree (No. 109)
Sept. 22, 2015 学一道算法题, 经常回顾一下. 第二次重温, 决定增加一些图片, 帮助自己记忆. 在网上找他人的资料, 不如自己动手. 把从底向上树的算法搞通俗一些. 先做一个例子: 9 ...
- [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 ...
- LeetCode Verify Preorder Sequence in Binary Search Tree
原题链接在这里:https://leetcode.com/problems/verify-preorder-sequence-in-binary-search-tree/ 题目: Given an a ...
- 【leetcode】Binary Search Tree Iterator
Binary Search Tree Iterator Implement an iterator over a binary search tree (BST). Your iterator wil ...
- leetcode98 Validate Binary Search Tree
题目: Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is define ...
随机推荐
- django自定义实现登录验证-更新版
django自定义实现登录验证 django内置的登录验证必须让开发者使用django内置的User模块,这会让开发者再某些方面被限制住 下面的模块是我自己自定义实现的django验证,使用方式和dj ...
- 运用JAVA的concurrent.ExecutorService线程池实现socket的TCP和UDP连接
运用JAVA的concurrent.ExecutorService线程池实现socket的TCP和UDP连接 最近在项目中可能要用到socket相关的东西来发送消息,所以初步研究了下socket的TC ...
- 【python实现卷积神经网络】池化层实现
代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...
- Nikto使用方法
Introduction Nikto是一款开源的(GPL)网站服务器扫描器,使用Perl基于LibWhisker开发.它可以对网站服务器进行全面的多种扫描,包括6400多个潜在危险的文件/CGI,检查 ...
- Web三维编程入门总结之二:面向对象的基础Web3D框架
本篇主要通过分析Tony Parisi的sim.js库(原版代码托管于:https://github.com/tparisi/WebGLBook/tree/master/sim),总结基础Web3D框 ...
- Delphi程序启动参数的读取
Delphi中有两个专门用于读取命令行参数的变量: Paramcount-->用于返回命令行参数的个数 Paramstr数组-->用于返回指定的命令行参数 示例代码 ...
- Mysql:小主键,大问题
今日格言:让一切回归原点,回归最初的为什么. 本篇讲解 Mysql 的主键问题,从为什么的角度来了解 Mysql 主键相关的知识,并拓展到主键的生成方案问题.再也不怕被问到 Mysql 时只知道 CR ...
- Go gRPC进阶-go-grpc-middleware使用(八)
前言 上篇介绍了gRPC中TLS认证和自定义方法认证,最后还简单介绍了gRPC拦截器的使用.gRPC自身只能设置一个拦截器,所有逻辑都写一起会比较乱.本篇简单介绍go-grpc-middleware的 ...
- jmeter DB2数据库连接与操作
1.需要把数据库连接jar包拷贝到 jmeter lib目录下 先创建一个数据库连接配置元件 2.添加jdbc请求(我用的后置处理器) 3.可以通过beanshell 对结果集进行操作 beanshe ...
- IDE使用GIT控制项目版本
IDEA本身继承GIT开发插件.只需要安装windows git客户端即可使用. check in project 检入项目 将新创建的项目上传到服务器. 对于git来说,空的目录不会上传到远程仓库. ...