树状数组

192 阅读1分钟

前缀和

初始化

时间复杂度:O(n),顺序扫描原数组

查询区间和

时间复杂度:O(1),`S[j]-S[i]`即为原数组i到j的区间和

单点修改

时间复杂度:O(n),修改S[i]到S[n]所有值

单点修改时关联性太强,导致修改太慢,弱化关联关系,以加快速度

lowbit函数

lowbit(i) 代表 i 这个数字,二进制表示的最后一位 1 的位权

示例

lowbit(8) = (1000)2 = 8

树状数组

C[i] 代表前 lowbit(i) 项和

image.png

前缀和查询

S[i] = S[i-lowbit(i)] + C[i]

image.png 如图,S[7] = C[7] + S[6] = C[7] + C[6] + C[4]

单点修改

下一个需要修改的值,i + lowbit(i)

image.png 如图,修改原数组 A[5] 的值,需要修改 C[5]、C[6]、C[8] 的值

实现

class FenwickTree {
  constructor(nums) {
    this.tree = new Array(nums.length + 1).fill(0)
    this.init(nums)
  }

  lowbit(n) {
    return n & (-n)
  }

  init(nums) {
    for (let i=1;i<this.tree.length;i++) {
      let count = this.lowbit(i)
      while(count) {
        this.tree[i] += nums[i - count]
        count--
      }
    }
  }

  // 在原数组下标为index的数值上加x
  add(i, x) {
    let index = i + 1
    while (index <= this.tree.length) {
      this.tree[index] += x
      index += this.lowbit(index)
    }
  }

  query(i) {
    let index = i + 1
    let sum = 0
    while (index) {
      sum += this.tree[index]
      index -= this.lowbit(index)
    }
    return sum
  }

  at(i) {
    return this.query(i) - this.query(i - 1)
  }

  output() {
    for(let i=0;i<this.tree.length;i++) {
      console.log(this.tree[i])
    }
  }
}