首页 > 其他分享 >树状数组

树状数组

时间:2023-02-23 16:02:27浏览次数:51  
标签:arr 树状 lowbit 复杂度 数组 区间

求区间和的问题

当前有一个包含n个元素的数组arr[n],需要不断地修改其中某一元素的值,以及查询某一区间的和。

最为原始的做法就是直接修改值,然后遍历求和,那么修改的时间复杂度就是\(O(1)\),查询的时间复杂度就是\(O(n)\)。

或者采用前缀和的形式,arr[i]记录的是0到i元素的和,这样查询区间和的复杂度就降为\(O(1)\)了,但是修改区间和的复杂度变为了\(O(n)\)。这样只适合查询很多,修改很少的场景。

树状数组

树状数组就是为了解决上面同时存在修改和查询的问题。树状数组用二进制的方式来划分区间,从而达到修改和查询的时间复杂度均为\(O(lgn)\)。

树状数组也是利用求前缀和的形式来算区间和,但是不是遍历求前缀和,而是将二进制划分的区间相加得到前缀和。

这里二进制的思想就是,对于任意一个数,其二进制表达都是0,1的序列,可以通过每一位上的1相加得到,比如

\[\begin{align} 6&=0110\notag \\ &=0100+0010\notag \\ \end{align}\]

要实现区间也能像二进制一样加起来,树状数组引入了lowbit函数,lowbit(i)i中最低的为1的二进制位。然后规定arr[i]表示从第i个元素往前数lowbit(i)个元素得到一个区间。

这样一来,要求任意的前缀和,只要不断地计算i = i - lowbit(i),直到i == 0就行了。这里执行的次数,就是i的二进制表示中1的数量,所以复杂度为\(O(lgn)\)。

对于修改某一元素,例如对于元素arr[i],其直接父层可以通过计算i = i + lowbit(i)计算得到,不断找影响到的父层,直到i > n就行了。所以复杂度也为\(O(lgn)\)。

代码示例(go)

package main

import "fmt"

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

// 将arr[idx]的值加上val
func add(arr []int, idx, val int) {
	idx++
	for idx <= len(arr) {
		arr[idx-1] += val
		idx += lowbit(idx)
	}
}

// 求[0,idx]的前缀和
func prefixSum(arr []int, idx int) int {
	var ans int
	idx++
	for idx != 0 {
		ans += arr[idx-1]
		idx -= lowbit(idx)
	}
	return ans
}

// 求[i,j]的区间和
func intervalSum(arr []int, i, j int) int {
	return prefixSum(arr, j) - prefixSum(arr, i-1)
}

// 在原数组上初始化树状数组
func initBIT(arr []int) {
	for i := len(arr) - 2; i >= 0; i-- {
		val := arr[i]
		arr[i] = 0
		add(arr, i, val)
	}
}

func main() {
	arr := []int{4, 2, 7, 5, 9, 1, 0, 3}
	initBIT(arr)
	for i := 0; i < len(arr); i++ {
		fmt.Println(prefixSum(arr, i))
	}
	for i := 0; i < len(arr); i++ {
		for j := i + 1; j < len(arr); j++ {
			fmt.Println(i, j, intervalSum(arr, i, j))
		}
	}
}

参考资料

标签:arr,树状,lowbit,复杂度,数组,区间
From: https://www.cnblogs.com/HachikoT/p/17148326.html

相关文章

  • 数组模拟队列
    //使用数组模拟队列-编写一个ArrayQueue类classArrayQueue{privateintmaxSize://表示数组的最大容量privateintfront;//队列头privateintrear;......
  • 3956.截断数组
    AcWing3956.截断数组(每日一题)-AcWing做题有感...  #include<iostream>usingnamespacestd;constintN=1e5+5;intn,s[N],temp,cnt=0;longlongans=0;......
  • 【JavaScript】26_数组的方法,对象的复制与数组的复制
    4、数组的方法​​https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array​​Array.isArray()用来检查一个对象是否是数组at()可以根据......
  • 【JavaScript】25_数组初步
    1、简介数组(Array)数组也是一种复合数据类型,在数组可以存储多个不同类型的数据数组中存储的是有序的数据,数组中的每个数据都有一个唯一的索引可以通过索引来操作获取数据数......
  • js 给树形(层级)数组添加层级标识
    树形数据,需要给每层的数据添加层级,如下:consttreeData=[{id:1,name:'a',children:[{id:101,name:'a1',children:null}]},{......
  • Java中的数组长度最大值为什么是 Integer.MAX_VALUE - 8
    Java中的数组长度最大值为什么是Integer.MAX_VALUE-8/* 因为数组容量使用int类型数据进行标识, 所以我们认为数组容量MAX是Integer.MAX_VALUE, 但是在编译器中定义......
  • 树状分级框架UI实例
    树状分级框架UI实例;(内容参考https://zhuanlan.zhihu.com/p/108485875) #coding:utf8#!/usr/bin/envpython#@author:9527importwximportwx.auifrompubsubimp......
  • 使数组中小于k的元素相邻的最小交换次数
    packagemainimport("bufio""fmt""math""os""sort""strconv""strings")/*描述:给出数字k,请输出所有结果小于k的整数组合到一起的最小......
  • 西电oj245 成绩统计 结构体数组使用
    #include<stdio.h>structstudent{//定义一个结构体数组 intnum; charname[11]; floatg1; floatg2; floatg3; floataver;};intmain(){ student......
  • 有序数组的平方(双指针)
    题目:给你一个按非递减顺序排序的整数数组nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。示例1:输入:nums=[-4,-1,0,3,10]输出:[0,1,9,16,100]解......