Binary Indexed Tree

树状数组或二元索引树(英语:Binary Indexed Tree),又以其发明者命名为Fenwick树,最早由Peter M. Fenwick于1994年以A New Data Structure for Cumulative Frequency Tables为题发表在SOFTWARE PRACTICE AND EXPERIENCE。其初衷是解决数据压缩里的累积频率(Cumulative Frequency)的计算问题,现多用于高效计算数列的前缀和, 区间和。它可以以在O(log n)的时间得到任意前缀和,并同时支持在O(log n)时间内支持动态单点值的修改。空间复杂度O(n)。

package IndexTree

import (
	"math/rand"
	"testing"
	"time"
)

/*
和堆很像,最终压缩到数组内

           1
         /  \
        2    3
      /  \  /  \
     4   5 6    7
   [1,2,3,4,5,6,7]

   HEAP
   parent: k
   left_child: 2k + 1
   right_child: 2k + 2

   BINARY_INDEXED_TREE

no 1bit                0
             /    /    |         \
            /    /     |          \
one 1bits  1    2      4           8
               /     /   \       /   \    \
two 1bits     3     5     6     9    10   12
                         /           |    / \
three 1bits             7           11   13 14


第0层 没有 1 bit位
第1层 有一个 1 bit位
第2层 有两个 1 bit位
第3层 有三个 1 bit位
...
第64层 有六十四个 1 bit位

parent to child

parent                    0000 (0)
                  /       /     \        \
children       0001(1) 0010(2)  0100(3) 1000(4)


从父节点找孩子:拥有父亲节点的二进制1比特个数加1个的所有数字

child to parent
                                 0000(0)
                                  /
                              0100(4)
                              /     \
                          0101(5)   0110(6)
                                     /
                                  0111(7)

从孩子节点找父节点: 孩子节点二进制去掉右侧的1比特的数字

[ 1     7     3     0     5     8     3     2     6     2      1      1      4      5  ]
  1     2     3     4     5     6     7     8     9     10     11     12     13     14

  1     8           11                     29
[1,1] [1,2]        [1,4]                  [1,8]
              3           5     13                6     8             10
            [3,3]       [5,5]  [5,6]            [9,9] [9,10]        [9,12]
                                      3                        1            4      9
                                    [7,7]                    [11,11]      [13,13][13,14]


[ 1     8      3     11    5      13   3    29     6      8     1      10    4      9 ]
  1     2      3     4     5      6    7    8      9      10    11     12    13     14

引入前缀和,每层都有一个新的和



[ 1     7     3     0     5     8     3     2     6     2      1      1      4      5  ]
  1     2     3     4     5     6     7     8     9     10     11     12     13     14

  1     8           11                     29 <------------------------
[1,1] [1,2]        [1,4]                  [1,8]                       |
              3           5     13                6     8             10 <---
            [3,3]       [5,5]  [5,6]            [9,9] [9,10]        [9,12]  |
                                      3                        1            4      9
                                    [7,7]                    [11,11]      [13,13][13,14]


SUM(13) = RANGE(1,8) + RANGE(9,12) + RANGE(13,13)
13: 拆分成 2进制的形式  13  =  8  +  4  +  1    即:   1101

*/

type IndexTree struct { // 下标从1开始!
	tree []int
	N    int
}

func NewIndexTree(size int) *IndexTree {
	return &IndexTree{
		tree: make([]int, size+1), // 0位置弃而不用
		N:    size,
	}
}

// 1 ~ index 累加和是多少  log(N)
func (it *IndexTree) sum(index int) int {
	res := 0
	for ; index > 0; index -= index & (-index) {
		res += it.tree[index]
	}
	return res
}

// index & -index : 提取出index最右侧的1出来
// index :           0011001000
// index & -index :  0000001000
// log(N)
func (it *IndexTree) add(index, d int) {
	for ; index <= it.N; index += index & (-index) {
		it.tree[index] += d
	}
}

type right struct {
	nums []int
	n    int
}

func NewRight(size int) *right {
	return &right{
		nums: make([]int, size+1),
		n:    size,
	}
}

func (r *right) sum(index int) int {
	res := 0
	for i := 1; i <= index; i++ {
		res += r.nums[i]
	}
	return res
}

func (r *right) add(index, d int) {
	r.nums[index] += d
}

func TestIndexTree(t *testing.T) {
	N := 100
	V := 100
	testTimes := 2000000
	tree := NewIndexTree(N)
	test := NewRight(N)
	t.Log("开始测试")
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < testTimes; i++ {
		index := rand.Int()%N + 1
		if rand.Float64() <= 0.5 {
			n := rand.Int() % V
			tree.add(index, n)
			test.add(index, n)
		} else {
			if tree.sum(index) != test.sum(index) {
				t.Log(tree.sum(index), test.sum(index))
				t.Log(tree)
				t.Log(test)
				panic("不相等")
			}
		}
	}
	t.Log("测试完成")
}

维基百科
https://zh.wikipedia.org/wiki/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值