【专题讲解】树状数组

本文详细介绍了树状数组的概念,包括其目的、特点及核心的lowbit、query和add函数。通过四个例题,展示了树状数组在解决快速求前缀和、修改单个数、移动次数计算、图腾问题等场景的应用。文章强调了树状数组在解决特定问题时的巧妙之处,需要通过多做题来积累经验。
摘要由CSDN通过智能技术生成

引言

树状数组的目的:快速求前缀和,修改某一个数

普通数组:快速求前缀和 O ( n ) O(n) O(n),修改某一个数 O ( 1 ) O(1) O(1)
树状数组:快速求前缀和,修改某一个数 都是 O ( l g n ) O(lgn) O(lgn)

树状数组的示意图

在这里插入图片描述

树状数组的特点在于,利用了二进制的性质,可以很方便的实现查询和修改。
通过上图我们可以清晰的看出,关键点在于每个索引转为为二进制以后的lowbit是多少,每个数组维护的区间和的区间长度恰好是lowbit,区间是从(index-lowebit, index],注意左开右闭。

主要由三个核心的函数构成

函数 lowbit(x)

主要是为了找到最低1位,因此lowbit(x) = x&-x

查询函数 query(x)

查找区间[1, x]的区间和。注意是从1开始的。这一步我们需要对所有与x相关的位置的值进行加和,也就是x的二进制中,为1的那些位置。

def query(x):
	res = 0
	while x>0:
		res += tree[x]
		x -= lowbit(x)
	return res

添加函数 add(x, value)

对某个值进行修改,并且还需要修改与之相关的全部的位置维护的区间和。相关位置是指x+lowbit(x)

def add(x, value):
	n = len(tree)
	while x < n:
		tree[x] += value
		x += lowbit(x)

例题1,移动k次能够得到的最小数字

题目的特点是,有n个可以且只能利用一次的数字,每次使用完会对下一次使用其他数字时候产生影响。因此可以考虑设置树状数组,每次利用query查找到使用该数字的代价,使用之后update数组,维护新的数组中其他元素的使用代价。

参考链接

例题2,图腾问题

题目需要明显得到某个数字左右两侧小于自己的数字个数,我们可以考虑一个巧妙的方法,对于数字大小的比较,我们直接把数字自身作为索引,在数组左侧的数字,都是该数字在原数组左侧中小于它的数字;在数组右侧的数字,都是该数字在原数组左侧中大于它的数字。正反两侧遍历就可以得到结果。

典型特征在于,统计了数字的个数。

一般树状数组的题目没有很明显的特征,不容易让人想到用树状数组。还是需要多做题积累经验。
在这里插入图片描述

这道题的特点在于,我们考虑把每个数字转化为坐标,我们在当前坐标时,只需要考虑已经遍历的部分有多少大于当前位置的,有多少小于当前位置的。通过正反两次遍历,可以找到对于每一个位置时,左右两侧有多少大于/小于的数字。最后只需要乘起来就可以。

说实话,这个想法很难想到。

      
def lowbit(x):  # 计算得到最低的1位
    return x & -x
    
def add(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值