树状数组的原理和区间和

news2024/11/15 9:14:35

目录

一、前言

二、树状数组的原理

1、杂论

2、从二叉树到树状数组

3、神奇的 lowbit(x) 操作

4、tree[ ]数组:将一维信息转换为树形信息存储

5、基于 tree[ ] 的计算

6、tree[]的更新(要加lowbit)

三、树状数组的应用

1、单点修改、区间查询

2、区间修改、区间查询(lanqiaoOJ1133)

(1)区间修改:利用差分(差分天然适合区间修改)

(2)区间查询(利用差分数组输出区间和)


一、前言

本文主要讲了树状数组的原理及其应用,涉及到了前缀和思想、差分思想。另外,补充另一篇关于树状数组的文章:lowbit和树状数组的理解与部分应用_吕同学的头发不能秃的博客-CSDN博客

二、树状数组的原理

1、杂论

  • 树状数组(Binary Indexed Tree, BIT),利用数的二进制特征进行检索的一种树状结构。
  • 一种真正的高级数据结构:二分思想、二叉树、位运算、前缀和
  • 高效!
  • 代码极其简洁!

【基本应用】

数列 a1,a2, ....,an,操作:

(1)修改元素 add(k, x):把ak加上x

(2)求和:sum(x) = a1 + ... +ax

区间和 ai + ... + aj = sum(j) - sum(i-1)

【不修改、只查询】

数列 a1, a2, ..., an,求区间和:ai +...+ aj

  • 数列是静态的,用前缀和计算区间和,特别高效。
  • 前缀和:sum[i] = a1 + ... + ai
  • 区间和:ai + ... + aj = sum[j] - sum[i-1]
  • 查询一次区间和,O(1)
a=[0,4,5,6,7,8,9,10,11,12,13]
sum=[0]*20
sum[1]=a[1]
for i in range(2,11):    #计算前缀和
    sum[i]=sum[i-1]+a[i]
print(sum)
for i in range(1,11):   #用前缀和反推计算数组a[]
    print(sum[i]-sum[i-1],end=' ')
print("[5,8]=",sum[8]-sum[4])   #查询区间和,例如查询[5,8]

【讨论】

如果数列是动态的,修改元素 add(k,x) 把ak加上x,复杂度是O(1);求区间和 sum(j)-sum(i-1),复杂度为O(n),求区间和的效率比较低。

【动态修改、求区间和:用树状数组】

数列是动态的

修改元素 add(k,x):把ak加上x。

求区间和:sum(j)-sum(i-1)

复杂度都是:O(logn)

def lowbit(x):
    return x&-x
def add(x,d):
    while x<n:
        tree[x]+=d
        x+=lowbit(x)
def sum(x):
    ans=0
    while x>0:
        ans+=tree[x]
        x-=lowbit(x)
    return ans

2、从二叉树到树状数组

3、神奇的 lowbit(x) 操作

  • lowbit(x) = x & -x
  • 功能:找到 x 的二进制数的最后一个 1

4、tree[ ]数组:将一维信息转换为树形信息存储

从 lowbit(x) 推出 tree[] 数组,所有的计算都基于 tree[]

令 m = lowbit(x)

定义tree[x]:把 ax 和它前面共 m 个数相加。

例:lowbit(6)=2,有 tree[6]=a5+a6

横线中的黑色表示 tree[x],等于横线上元素相加的和

5、基于 tree[ ] 的计算

(1)求和 sum=a1 + ... + ax

利用 tree[] 数组求 sum,例如:

sum[8] = tree[8]

sum[7] = tree[7] + tree[6] + tree[4]

sum[9] = tree[9] + tree[8]

以上关系是如何得到的?借助lowbit(x)

【sum的计算】(要减lowbit)

例:sum[7] = tree[7] + tree[6] + tree[4]

(1)从 7 开始,加上 tree[7];

(2)7-lowbit(7)=6,加上tree[6];

(3)6-lowbit(6)=4,加上tree[4];

(4)4-lowbit(4)=0,结束。

写出数的二进制进行加减你会更加清晰其中的道理,以及为什么要这么设计。

sum() 的复杂度?

O(logn)

非常好!

6、tree[]的更新(要加lowbit)

更改 ax,和它相关的 tree 都会变化。

例如改变 a3,那么 tree[3]、 tree[4]、tree[8]... 都会改变。

影响哪些 tree[ ]?仍然利用 lowbit(x):

(1)更改tree[3];

(2)3+lowbit(3)=4,更改 tree[4];

(3)4+lowbit(4)=8,更改 tree[8];

(4)直到最后的 tree[n]。

复杂度?

O(logn)

非常好!

三、树状数组的应用

1、单点修改、区间查询

【题目描述】

数列 a1, a2, ..., ai,操作:

(1)修改元素 add(k, x):把ak加上x。

(2)求和:

sum(x) = a1 +... +ax

区间和 ai + ... + aj = sum(j) - sum(i-1)

【代码】

def lowbit(x):
    return x&-x
def add(x,d):   #给元素a[x]加上d
    while x<=N:
        tree[x]+=d
        x+=lowbit(x)
def sum(x):     #返回前缀和sum
    ans=0
    while x>0:
        ans+=tree[x]
        x-=lowbit(x)
    return ans

N=1000
tree=[0]*N
a=[0,4,5,6,7,8,9,10,11,12,13]
for i in range(1,11):   #计算tree[]数组
    add(i,a[i])
print("old:[5,8]=",sum(8)-sum(4))   #查询区间和,例如查询[5,8]
add(5,100)                        #模拟一次修改:a[5]=a[5]+100
print("new:[5,8]=",sum(8)-sum(4))   

2、区间修改、区间查询(lanqiaoOJ1133)

【题目描述】

给定一个长度为 N 的数组 a,初值为 a1, a2, ..., aN

有 Q 个操作,操作有两种:

1 L R d:将区间 [L,R] 内每个数加上 d。

2 L R:输出区间 [L,R] 内每个数的和。

【输入描述】

第 1 行是整数 N、M,表示数组 a 的长度和操作个数。1<=n, m<=10^5

第 2 行包含 N 个非负整数 a1, a2, ...aN,表示数组 a 的初值

第 3~M+2 行每行表示一个操作

【输出描述】

输出每行一个整数,表示查询的答案

【输入样例】

5 5

1 2 3 4 5

2 1 2

1 2 3 1

2 1 3

1 1 5 1

2 1 5

【输出样例】

3

8

22

【输入处理代码】

n,m=map(int,input().split())
#old=0
a=[0]+[int(i) for i in input().split()]     #a[0]不用

for _ in range(m):
    g=[int(i) for i in input().split()]
    if g[0]==1:     #区间修改
        L,R,d=g[1],g[2],g[3]
    else:           #区间询问
        L,R=g[1],g[2]

(1)区间修改:利用差分(差分天然适合区间修改)

一维差分数组 D[k]=a[k]-a[k-1],即原数组 a[] 的相邻元素的差

差分数组能提高修改的效率。

把区间 [L,R] 内每个元素 a[] 加上 d,只需把对应的 D[] 做以下操作:

(1)把 D[L] 加上 d:D[L] += d

(2)把 D[R+1] 减去 d:D[R+1] -= d

 利用 D[],能极快解决修改区间 [L,R] 内元素的目的。原来需要 O(n) 次计算,现在只需要O(1)

说明:前缀和 a[x]=D[1]+D[2]+...+D[x],有:

(1)1<=x<L,前缀和 a[x] 不变;

(2)L<=x<=R,前缀和 a[x] 增加了 d;

(3)R<x<=N,前缀和 a[x] 不变,因为被 D[R+1] 中减去的 d 抵消了。

(2)区间查询(利用差分数组输出区间和)

  • 推导区间和,看它和求前缀和有没有关系,如果有关系,就能用树状数组。

  • 最后的公式有两个前缀和
  • 用两个树状数组分别处理:一个实现 Di,一个实现 (i - 1)Di。

【树状数组初始化】

(1)区间修改 D[k] = a[k] - a[k-1]

(2)区间查询

def lowbit(x):
    return x&-x
def update1(x,d):   #修改元素a[x],a[x]=a[x]+d
    while x<=N:
        tree1[x]+=d
        x+=lowbit(x)
def update2(x,d):
    while x<=N:
        tree2[x]+=d
        x+=lowbit(x)
def sum1(x):
    ans=0
    while x>0:
        ans+=tree1[x]
        x-=lowbit(x)
    return ans
def sum2(x):
    ans=0
    while x>0:
        ans+=tree2[x]
        x-=lowbit(x)
    return ans

N=100010
tree1=[0]*N
tree2=[0]*N   #2个差分树状数组
n,m=map(int,input().split())
old=0
a=[0]+[int(i) for i in input().split()]     #a[0]不用

for i in range(1,n+1):
    update1(i,a[i]-old)     #差分数组原理,初始化
    update2(i,(i-1)*(a[i]-old))
    old=a[i]

for _ in range(m):
    g=[int(i) for i in input().split()]
    if g[0]==1:     #区间修改
        L,R,d=g[1],g[2],g[3]
        update1(L,d)        #第1个树状数组
        update1(R+1,-d)     
        update2(L,d*(L-1))  #第2个树状数组
        update2(R+1,-d*R)   #d*R=d*(R+1-1)
    else:           #区间询问
        L,R=g[1],g[2]
        print(R*sum1(R)-sum2(R)-(L-1)*sum1(L-1)+sum2(L-1))

以上,树状数组的原理和区间和

祝好

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/175779.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

流媒体方案之Nginx

1.Nginx可以作为流体服务器。2三种web服务器的比较3.推流端: FFmpeg使用RTMP协议向Nginx推流拉流端: •VLC播放器使用RTMP或HTTPFLV协议从Nginx拉流•浏览器使用HTTPFLV协议从Nginx拉流(安装flv.js)4.有两种方法&#xff1a;下载源码&#xff0c;手工编译使用Buildroot&#xf…

Redis分布式锁 | 黑马点评

目录 一、分布式锁概述 二、基于Redis的分布式锁 1、思路分析 2、初级版本 3、误删问题 4、改进分布式锁 5、原子性问题 6、使用Lua脚本解决原子性问题 7、setnx实现分布式锁存在问题 三、Redisson 1、Redisson快速入门 2、Redisson可重入锁原理 3、Redisson可重试…

从某一点出发沿任意一方向旋转矩阵计算思考与实现

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 上期讲到 绕任一向量旋转矩阵计算思考与实现 点击前往 点击前往 问题提出 之前讲到绕任一向量旋转矩阵实现&#xff0c;原来的向量都是从原点出发&#xff0c;现在把…

Ajax面试题目

更多Ajax面试题目可以查看专栏内容 1.AJAX应用和传统Web应用有什么不同&#xff1f; 传统的web前端与后端的交互中&#xff0c;浏览器直接访问Tomcat的Servlet来获取数据。Servlet通过转发把数据发送给浏览器。当我们使用AJAX之后&#xff0c;浏览器是先把请求发送到XMLHttpR…

Swift之struct二进制大小分析

随着Swift的日渐成熟和给开发过程带来的便利性及安全性&#xff0c;京喜App中的原生业务模块和基础模块使用Swift开发占比逐渐增高。本次讨论的是struct对比Class的一些优劣势&#xff0c;重点分析对包体积带来的影响及规避措施。 一、基础知识 1、类型对比 引用类型&#xff…

独立看门狗与窗口看门狗

定义 看门狗的本质是一个定时器&#xff0c;在启动后&#xff0c;需要在一定时间内再给它一个信号&#xff0c;俗称“喂狗”&#xff0c;如果没有按时“喂狗”&#xff0c;说明MCU可能处于非正常状态&#xff0c;这时看门狗就向MCU发送个复位信号&#xff0c;使整个系统重启&a…

51单片机数码管显示

文章目录前言一、数码管简介二、数码管原理图三、数码管显示原理四、静态数码管代表编写五、动态数码管总结前言 这篇文章将介绍数码管的显示其中包含了动态数码管和静态数码管两种。 一、数码管简介 数码管其实就是由多个发光二极管封装在一起组成“8”字型的器件当分别点亮…

【数据结构】超详细——堆的实现

一、堆的概念及性质 1.1 什么是堆&#xff1f; 堆是一种完全二叉树&#xff08;具体在下一章讲述&#xff09;&#xff0c;若二叉树的深度h&#xff0c;除了第h层外其余各层节点数满了&#xff0c;只有第h层缺额且该层结点靠左&#xff1b;任何一个数组可以看作完全二叉树&…

【14】C语言_函数简介

目录 1、C语言中函数的分类: 2、库函数 3、自定义函数 1、C语言中函数的分类: 1.库函数 2.自定义函数 2、库函数 为什么会有库函数? 1.我们知道在我们学习C语言编程的时候&#xff0c;总是在一个代码编写完成之后迫不及待的想知道结果&#xff0c;想把这个结果打印到我们的屏…

ESP32设备驱动-LX1972可见光传感器驱动

LX1972可见光传感器驱动 1、LX1972介绍 LX1972 是一款低成本硅光传感器,其光谱响应非常接近人眼。专利电路在 520nm 处产生峰值光谱响应,IR 响应小于峰值响应的 5%,高于 900nm。 光电传感器是一个 PIN 二极管阵列,具有线性、准确和非常可重复的电流传递函数。 芯片上的…

扫盲-从零开始搭建阿里云流媒体服务器/音视频编解码/

1.基础概念 2.简单模式-HTTP文件服务器 存储采用NAS&#xff0c;服务器配置 采用FASTDFS (192条消息) Linux新手入门系列&#xff1a;FastDFS单机部署一键安装脚本_IT小胖豆的博客-CSDN博客 有几个坑&#xff1a; 常用命令&#xff1a; tail -20f /usr/local/nginx/logs/er…

3小时精通opencv(三)图片裁剪与形状绘制

3小时精通opencv(三)图片裁剪与形状绘制 参考视频资源:3h精通Opencv-Python 文章目录3小时精通opencv(三)图片裁剪与形状绘制图片裁剪绘制形状绘制直线绘制矩形绘制圆形绘制文字整体代码图片裁剪 图片裁剪不需要使用opencv中特有的函数, 对于opencv中读取到的图像, 直接当做矩…

15. python数据类型转换

1. 隐式类型转换 - 自动完成 在隐式类型转换中&#xff0c;Python 会自动将一种数据类型转换为另一种数据类型&#xff0c;不需要我们去干预。 (1) 以下实例中&#xff0c;我们对两种不同类型的数据进行运算&#xff0c;较低数据类型&#xff08;整数&#xff09;就会转换为较…

java 探花交友项目day4 MongoDB

数据库表 接口定义 其他都比较简单 我们讲黑名单查询页面的设计 DubboService public class BlackListApiImpl extends ServiceImpl<BlackListMapper,BlackList> implements BlackListApi{Autowiredprivate BlackListMapper blackListMapper;Autowiredprivate UserInf…

Allegro如何输出坐标文件操作指导

Allegro如何输出坐标文件操作指导 PCB在SMT的时候会需要用坐标文件,Allegro支持输出坐标文件,如下图 具体操作如下 选择Tools选择report出现repor

LINUX学习之正则表达式(十二)

普通正则 元字符 元字符匹配描述.匹配除了换行符以外的任意单个字符*前导字符出现0次或连续多次.*任意长度字符^行首(以…开头)$行尾(以…结尾)^$空行[]匹配括号里任意单个字符或一组单个字符[^]匹配不包含括号里任一单个字符或一组单个字符^[]匹配以括号里任意单个字符或一组…

Python技能树-推导式

Python 列表推导式(1) Python 独步天下的推导式表达式&#xff0c;使用列表推导式过滤出偶数列表 # -*- coding: UTF-8 -*- if __name__ __main__:list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]print()print("# 使用列表推导式过滤出偶数")# TODO(you): 请在此实现过滤代…

Allegro如何添加平衡铜操作指导

Allegro如何添加平衡铜操作指导 PCB在加工的时候,工厂会添加平衡铜,Allegro支持自动加上平衡铜,如下图 具体操作如下 选择Manufacture点击Thieving

比较器: Comparable 与 Comparator 区别

比较器&#xff1a; Comparable 与 Comparator 区别 每博一文案 师父说: 人不能精得过火&#xff0c;太精明的人往往让人生厌&#xff0c;人也别傻的可怜&#xff0c;一腔热血付出却白忙一场。 太精明的人&#xff0c;凡事都想要争个明明白白&#xff0c;每一分钱都要和人计较…

macOS Ventura 13.1 系统问题:掉电快 充电慢

今年一月份升级了 MBA 的系统&#xff0c;之后的笔记本&#xff1a; 使用过程&#xff1a;电量不禁用&#xff0c;掉电很快。 充电过程&#xff1a;很慢。而且存在一定几率&#xff1a;电量充到某个值&#xff08;如30%&#xff09;之后不管再充多久还是这个电量值。 系统信息…