简介
树状数组类是线段树,但是却又不同,其对于节点的更改,和查找集合是十分方便的
原理
假设,我们当前的数组为a
,构成的线状数组为c
那么
![未命名文件 (3)](https://i-blog.csdnimg.cn/blog_migrate/fbbc61f7a613a818fcfa31f65849607a.png)
即:
c[1]=a[1]
c[2]=a[1]+a[2]
c[3]=a[3]
c[4]=a[1]+a[2]+a[3]+a[4]
c[5]=a[5]
c[6]=a[5]+a[6]
c[7]=a[7]
c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]
我们换一个角度观察:
c[0001]=a[1]
c[0010]=a[1]+a[2]
c[0011]=a[3]
c[0100]=a[1]+a[2]+a[3]+a[4]
c[01001]=a[5]
c[0110]=a[5]+a[6]
c[0111]=a[7]
c[1000]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]
也就是:
c
[
i
]
=
a
[
i
−
2
k
+
1
]
+
.
.
.
+
a
[
i
]
k
为
i
的
二
进
制
中
末
尾
连
续
0
的
个
数
c[i]=a[i-2^k+1]+ ... + a[i]\\ k为i的二进制中末尾连续0的个数
c[i]=a[i−2k+1]+...+a[i]k为i的二进制中末尾连续0的个数
如:i=8
时,k=3
,则:c[1000]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]
求和
很明显
sum[5]=a[1]+a[2]+a[3]+a[4]+a[5]
也就是 sum[5]=c[4]+c[5]
即:sum[101]=c[100]+c[101]
也就是当前的sum为每次去除二进制i
后末尾的1
,直到为0
的合
代码展示
// 取出最低位的1
func lowbit(i int) int {
// 比如:6 = 0110
// -6 = 1001+1 = 1010
// 6&(-6) = 0010 = 2
return i & (-i)
}
// 求和
// sum(7) = a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]
// c[4] = a[1] + a[2] + a[3] + a[4];c[6] = a[5] + a[6] ; c[7] = a[7]
// sum(7) = c[4] + c[6] + c[7]
// 即:sum(111) = c[100] + c[110] + c[111]
func getSum(x int, c []int) int {
sum := 0
// 每次减去末尾的1
for i := x; i > 0; i -= lowbit(i) {
sum += c[i]
}
return sum
}
查询
跟求和类似,直接上代码
// 下标为x的增加y
// 如 x = 1 = 0001 ;c[1],c[2],c[4],c[8]则需要更新
// 即:0001、0010、0100、1000
// 更新即查询的逆过程
func add(x, y int, c []int) {
for i := x; i < len(c); i += lowbit(i) {
c[i] += y
}
}
整体代码
// c[i] = a[i-2^k+1] + ... + a[i]
// k 为i的二进制下末尾连续的0的个数
// 如:c[6] = a[5] + a[6] 6 = 0110 k = 1
// 取出最低位的1
func lowbit(i int) int {
// 比如:6 = 0110
// -6 = 1001+1 = 1010
// 6&(-6) = 0010 = 2
return i & (-i)
}
// 求和
// sum(7) = a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]
// c[4] = a[1] + a[2] + a[3] + a[4];c[6] = a[5] + a[6] ; c[7] = a[7]
// sum(7) = c[4] + c[6] + c[7]
// 即:sum(111) = c[100] + c[110] + c[111]
func getSum(x int, c []int) int {
sum := 0
// 每次减去末尾的1
for i := x; i > 0; i -= lowbit(i) {
sum += c[i]
}
return sum
}
// 下标为x的增加y
// 如 x = 1 = 0001 ;c[1],c[2],c[4],c[8]则需要更新
// 即:0001、0010、0100、1000
// 更新即查询的逆过程
func add(x, y int, c []int) {
for i := x; i < len(c); i += lowbit(i) {
c[i] += y
}
}
func created(arr []int) []int {
n := len(arr)
c := make([]int, n)
for i := 0; i < n; i++ {
// 跟更新类似
c[i] += arr[i]
j := i + lowbit(i)
// 更新其父节点
if j < n {
c[j] += c[i]
}
}
return c
}
例题:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5限制:
0 <= 数组长度 <= 50000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof
func reversePairs(nums []int) int {
n := len(nums)
tmp := make([]int, n)
copy(tmp, nums)
sort.Ints(tmp) // 排序
for i := 0; i < n; i++ {
// nums = {3,4,2}
// 如,tmp={2,3,4}
// nums = {2,3,1}
nums[i] = sort.SearchInts(tmp, nums[i]) + 1 // 找到对应的下标 +1
}
bit := BIT{n: n, tree: make([]int, n+1)}
ans := 0
// 从后往前遍历. 现在nums为排序后的对应的index
for i := n - 1; i >= 0; i-- {
ans += bit.query(nums[i] - 1)
bit.update(nums[i])
}
return ans
}
type BIT struct {
n int
tree []int
}
func (b BIT) lowbit(x int) int {
return x & (-x)
}
func (b BIT) query(x int) int {
ret := 0
for x > 0 {
ret += b.tree[x]
x -= b.lowbit(x)
}
return ret
}
// 对应的+1
// 这里可能会很大
func (b BIT) update(x int) {
for x <= b.n {
b.tree[x]++
x += b.lowbit(x)
}
}