数据结构 (一): 树状数组 / 二叉索引树 / BIT

本文介绍了树状数组(二叉索引树,BIT)的概念,强调了其在区间求和操作上的优势。文章详细阐述了lowbit的概念及其在二叉树层级中的作用,并解析了BIT的组织方式,展示了一个节点如何存储区间和。此外,还提供了C语言实现BIT的代码示例,并提醒读者BIT的下标通常从1开始。

简介

树状数组的学名是二叉索引树, Binary Indexed Tree, BIT, 下文以 BIT 简称.
BIT 是一种特殊组织过的数组, 它和普通数组的唯一区别是:
BIT 的核心任务是快速求区间和, 而数组的核心任务是随机存取, 因此, 二者在只是时间复杂度上有所不同:

数据结构单点修改单点访问区间求和
普通数组 O ( 1 ) O(1) O(1) O ( 1 ) O(1) O(1) O ( N ) O(N) O(N)
BIT O ( l o g N ) O(logN) O(logN) O ( l o g N ) O(logN) O(logN) O ( l o g N ) O(logN) O(logN)

由此可见, 在单点存取时普通数组是最快的 (没人比它更快), 而对于区间的求和操作, BIT 有很大的优势.

lowbit

一个数字的 lowbit 是它二进制数最右边1的值, 比如
l o w b i t ( 3 ) = l o w b i t ( 11 ) = 1 = 1 lowbit(3) = lowbit(11) = 1 = 1 lowbit(3)=lowbit(11)=1=1
l o w b i t ( 6 ) = l o w b i t ( 110 ) = 10 = 2 lowbit(6) = lowbit(110) = 10 = 2 lowbit(6)=lowbit(110)=10=2
l o w b i t ( 12 ) = l o w b i t ( 1100 ) = 100 = 4 lowbit(12) = lowbit(1100) = 100 = 4 lowbit(12)=lowbit(1100)=100=4
我们观察一下 1 , 2 , . . . , 10 {1, 2, ..., 10} 1,2,...,10 的 lowbit 的值:
l o w b i t ( 1 , 2 , . . . , 10 ) = { 1 , 2 , 1 , 4 , 1 , 2 , 1 , 8 , 1 , 2 } lowbit(1, 2, ... , 10) = \{1, 2, 1, 4, 1, 2, 1, 8, 1, 2\} lowbit(1,2,...,10)={1,2,1,4,1,2,1,8,1,2}
发现了没, 这个 lowbit 序列包含了一种二叉树的结构, lowbit 相同的位置都处在同一层. 这样我们就可以根据一个位置的 lowbit 得到它在二叉树的层级信息.

获取一个数字 lowbit 的公式:

int lowbit(int x) {return x&-x;}

BIT 的组织方式

普通数组每个位置的值就是这个位置元素的值, 而 BIT 不一样, 它每个位置的值是一个区间和, 组织方式如下:

也就说, 一个节点的值是它和它所有左孩子的和
从这样的组织方式我们可以看出:

  1. 一个元素的值被包含在它和它所有的右父亲中
  2. 以一个元素为结尾的前缀和是它所有左父亲的和 (比如前缀和 a[0:7] 的值等于a[7] + a[6] + a[4], 如图所示)

显然, 更新一个元素的值和区间求和的复杂度都是 O ( l o g N ) O(logN) O(logN), 因为都要对它一半的父亲操作.

代码 ( C 语言 )

#include <stdio.h>
#include <string.h>

#define MAXN 1000
int bit[MAXN];

int lowbit(int x) {return x&-x;}
void init() {memset(bit, 0, sizeof(bit));}

// 修改单个位置的值
void add(int pos, int val) {
	while (pos < MAXN) {
		bit[pos] += val;
		pos += lowbit(pos);
	}
}
// 返回前缀和, 即 sum(a[0:pos])
int prefixSum(int pos) {
	int sum = 0;
	while (pos > 0) {
		sum += bit[pos];
		pos -= lowbit(pos);
	}
	return sum;
}
// 返回区间和, 即 sum(a[from:to])
int rangeSum(int from, int to) {
	return prefixSum(to) - prefixSum(from-1);
}

注意事项

BIT 的第一个元素应该是下标 1 开始的, 因为 0 没有 lowbit, 因为 0 的二进制里没有 1.

总结

学!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值