区间信息维护与查询【树状数组 】 - 原理1 一维树状数组

news2024/10/6 20:25:09

区间信息维护与查询【树状数组 】 - 原理1 一维树状数组

【原理1】 一维树状数组

有一个包含n 个数的数列2, 7, 1, 12, 5, 9 …,请计算前i 个数的和值,即前缀和sum[i ]=a [1]+a [2]+…+a [i ](i =1, 2, …, n)。该怎么计算呢?一个一个加起来怎么样?

sum = 0;
for(int k = 1; k <= i ; k ++){
	
	sum += a[k];
}

若用这种办法,则计算前n 个数的和值需要O (n )时间。而且若对a [i ]进行修改,则对sum[i ], sum[i +1], …, sum[n ]都需要修改,在最坏的情况下需要O (n )时间。

当n 特别大时效率很低。

树状数组可以高效地计算数列的前缀和,其查询前缀和与点更新(修改)操作都可以在O (logn )时间内完成,那么树状数组是怎么巧妙实现这些的呢?

① 树状数组的由来

树状数组引入了分级管理制度且设置了一个管理小组,管理小组中的每个成员都管理一个或多个连续的元素。例如,在数列中有9个元素,分别用a [1], a [2], …, a [9]存储,还设置了一个管理小组c[]。

在这里插入图片描述

管理小组的每个成员都存储其所有子节点的和。

  • c [1]:存储a [1]的值。
  • c [2]:存储c [1]、a [2]的和值,相当于存储a [1]、a [2]的和值。
  • c [3]:存储a [3]的值。
  • c [4]:存储c [2]、c [3]、a [4]的和值,相当于存储a [1]、a[2]、a [3]、a [4]的和值。
  • c [5]:存储a [5]的值。
  • c [6]:存储c [5]、a [6]的和值,相当于存储a [5]、a [6]的和值。
  • c [7]:存储a [7]的值。
  • • c [8]:存储c [4]、c [6]、c [7]、a [8]的和值,相当于存储a[1]~a [8]的和值。
  • c [9]:存储a [9]的值。

从上图可以看出,这个管理数组c []是树状的,因此叫作树状数组。怎么利用树状数组求前缀和及点更新呢?

[1] 查询前缀和

若想知道sum[7],则只需c [7]加上左侧所有子树的根即可,即sum[7]=c [4]+c [6]+c [7]。

在这里插入图片描述

  • sum[4]:左侧没有子树,直接找c [4]即可,sum[4]=c [4]。
  • sum[5]:左侧有一颗子树,其根为c [4],sum[5]=c [4]+c[5]。
  • sum[9]:左侧有一棵子树,其根为c [8],sum[9]=c [8]+c[9]。

[2] 点更新

点更新指修改一个元素的值,例如对a [5]加上一个数y ,则需要更新该元素的所有祖先节点,即c [5]、c [6]、c [8],令这些节点都加上y 即可,对其他节点都不需要修改。

在这里插入图片描述

为什么只修改其祖先节点呢?因为当前节点只和祖先有关系,和其他节点没有关系。

  • c [5]:存储a [5]的值,修改a [5]加上y ,因此c [5]也要加上y 。
  • c [6]:存储c [5]、a [6]的和值(a [5]、a [6]),a [5]加上y ,c [6]也要加上y 。
  • c [8]:存储c [4]、c [6]、c [7]、a [8]的和值(~a [1]a[8]),a [5]加上y ,c [8]也要加上y 。

那么这个管理数组(树状数组)是怎么得来的呢?

② 树状数组的实现

树状数组,又叫作二进制索引树(Binary Indexed Trees),通过二进制分解划分区间。那么c [i ]存储的是哪些值?

[1] 区间长度

若i 的二进制表示末尾有k 个连续的0,则c [i ]存储的区间长度为2^k ,从a [i ]向前数2^k 个元素,即c [i ]=a [i -2^k +1]+a [i-2^k +2]+…+a [i ]。

在这里插入图片描述

例如:i =6,6的二进制表示为110,末尾有1个0,即c [6]存储的值区间长度为2(2^1 ),存储的是a [5]、a [6]的和值,即c [6]=a[5]+a [6]。

i =5,5的二进制表示为101,末尾有0个0,即c [5]存储的值区间长度为1(2^0 ),它存储的是a [5]的值,即c [5]=a [5]。

怎么得到这个区间的长度呢?若i 的二进制表示末尾有k 个连续的0,则c [i ]存储的值区间长度为2^k ,换句话说,区间长度就是i 的二进制表示下最低位的1及它后面的0构成的数值。例如i =20,其二进制表示为10100,末尾有两个0,区间长度为2^2 (4),其实就是10100最低位的1及其后面的0构成的数值100(该数为二进制,其十进制为4)。

在这里插入图片描述

怎么得到100呢?可以先把10100取反,得到01011,然后加1得到01100,此时,最低位的1仍然为1,而该位前面的其他位与原值相反,因此与原值10100进行与运算即可。

在这里插入图片描述

  • 取反运算(~):1变成0,0变成1。
  • 与运算(&):两位都是1,则为1,否则为0。

在计算机中二进制数采用的是补码表示,-i 的补码正好是i 取反加1,因此(-i )&i 就是区间的长度。若将c [i ]存储的值区间长度用lowbit(i )表示,则lowbit(i )=(-i )&i 。

算法代码:

int lowbit(int i){
	return (-i) & i;
}

[2] 前驱和后继

直接前驱:c [i ]的直接前驱为c [i -lowbit(i )],即c [i ]左侧紧邻的子树的根。

直接后继:c [i ]的直接后继为c [i +lowbit(i )],即c [i ]的父节点。

前驱:c [i ]的直接前驱、其直接前驱的直接前驱等,即c [i ]左侧所有子树的根。

后继:c [i ]的直接后继,其直接后继的直接后继等,即c [i ]的所有祖先。

在这里插入图片描述

c [7]的直接前驱为c [6],c [6]的直接前驱为c [4],c [4]没有直接前驱;c [7]的前驱为c [6]、c [4]。

c [5]的直接后继为c [6],c [6]的直接后继为c [8],c [8]没有直接后继;c [5]的后继为c [6]、c [8]。

[3] 查询前缀和

前i 个元素的前缀和sum[i ]等于c [i ]加上c [i ]的前驱,sum[7]等于c [7]加上c [7]的前驱,c [7]的前驱为c [6]、c [4],因此sum[7]=c [7]+c [6]+c [4]。

算法代码:

int sum(){ //求 前缀和a[1] .. a[i]
	
	int s = 0;
	for( ; i > 0 ; i -= lowbit(i)){ //直接前驱 i -= lowbit(i)
		s += c[i];
	}
	return s;
}

[4] 点更新

若对a [i ]进行修改,令a [i ]加上一个数z ,则只需更新c [i ]及其后继(祖先),即令这些节点都加上z 即可,不需要修改其他节点。修改a [5],另其加上2,则只需c [5]+2,对c [5]的后继分别加上2,即c [6]+2、c [8]+2。

算法代码:

void add(int i , int z){ //a[i] 加上z
	for(; i <= n ; i += lowbit(i)){ //直接后继,即父节点 i += lowbit(i)
		c[i] += z;
	}
}

注意:树状数组的下标从1开始,不可以从0开始,因为lowbit(0)=0时会出现死循环。

[5] 查询区间和

若求区间和值a [i ]+a [i +1]+…+a [j ],则求解前j 个元素的和值减去前i -1个元素的和值即可,即sum[j ]-sum[i -1]。

算法代码:

int sum(int i , int j){ // 求区间和a[i] .. a[j]
	return sum(j) - sum(i - 1);
}

③ 算法分析

树状数组是通过二进制分解划分区间的。树状数组的性能与n 的二进制位数有关,n 的二进制位数为 ⌊ logn ⌋ + 1 ,⌊ x ⌋ 表示向下取整,即取小于或等于x 的最大整数。⌊ log5 ⌋ =2,5的二进制位数为3位;⌊ log8 ⌋ =3,8的二进制位数为4。

如何求解树状数组的高度呢?树状数组底层的叶子是c [1],因此从开始一直找其后继(祖先)直到树根,就是树状数组的高度。

c [1]-c [2^1 ]-c [2^2 ]-c [2^3 ]-…-c [n ],每次都是2倍增长,假设n =2^x,则x =logn ,因此树高h =O (logn )。更新时,从叶子更新到树根,执行的次数不超过树的高度,因此更新的时间复杂度为O (logn )。

在这里插入图片描述

查询前缀和时,需要不停地查找前驱,那么前驱最多有多少个呢?

n 的二进制数有k = ⌊ logn ⌋ + 1 位,在最多的情况下,每一位都是1,则n =“111…1”可以被表示为n =2^(k -1) +2^(k -2) +…+2^1 +2^0 。7=“111”=2^2 +2^1 +2^0 ,c [7]的前驱为c [7-2^0 ]、c[7-2^0 -2^1 ]、c[7-2^0 -2^1 -2^2 ],最后一个为c [0],表示不存在,因此c [7]的前驱为c [6]、c [4]。前驱的个数与n 的二进制数的位数有关,不超过O(logn ),

因此查询前缀和的时间复杂度为O (logn ),即树状数组修改和查询的时间复杂度均为O (logn )。

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

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

相关文章

RISC-V入门(基础概念+汇编部分) 基于 汪辰老师的视频笔记

RISC-V入门 [完结] 循序渐进&#xff0c;学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春 RISC-V 部分作业答案 参考 RISC-V ISA 基本介绍 历史简介 自由&#xff08;Free&#xff09;与开放&#xff08;Open&#xff09; RISC-V 念作 “risk-five”&#xff0c;代表着Berke…

JSON.stringify() / JSON.parse() / JSON 真是个好东西

目录 1. JSON 基本概念 1.1 JavaScript 对象表示法 1.2 JSON 文件 1.3 JSON 语法 2. XML VS JSON 2.1 共同点 2.2 不同点 2.3 使用步骤对比 3. JSON.parse() 3.1 使用介绍 3.2 使用 reviver 参数&#xff0c;将 JSON 对象解析出来的 string 转化为 Date 4. JSON.st…

【ML特征工程】第 4 章 :特征缩放的影响:从词袋到 Tf-Idf

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

【知识网络分析】耦合网络(bibliographic coupling)

耦合网络(bibliographic coupling) 1 读取本地文献并构建耦合网络数据集2 网络数据集精简3 中心点附近网络子群绘制4 求解网络图中节点中心度相关指标数值1 读取本地文献并构建耦合网络数据集 新建一个notebook文件,第一步导入功能包和数据集,案例中仍使用2020-2021年WOS数…

第八章《Java高级语法》第2节:补码

在Java语言中,使用补码的形式来表示数字。补码是计算机表示数字的一种规则或者是表示形式,它的算法很简单:用最左边的一个二进制位表示数字的正负,0表示正数,用1表示负数,专业上把表示符号的这个二进制位叫做“符号位”。符号位后面剩余的二进制位表示数字本身。 对于正…

PyQt5 QWebEngineView网页交互

QWebEngineView网页交互QWebEngineView常用方法加载并显示外部的Web页面加载并显示本地的Web页面加载并显示嵌入的HTML代码QWebEngineView常用方法 方法描述load(QUrl url)加载指定的URL并显示setHtml(QString &html)将网页视图的内容设置为指定的HTML内容 核心代码&#…

chapter2——时钟和复位

目录1.同步设计2.推荐的设计技术3.时钟方案4.门控时钟方法学5.复位信号的设计策略6.控制时钟偏移1.同步设计 在同步设计中由单个主时钟和单个主置位/复位信号驱动设计中所有的时序器件&#xff0c;对于ASIC的时钟域控制最安全的方法就是同步设计。 避免使用行波计数器 由于第…

Python 实现自动化测试 dubbo 协议接口

前言 在工作或学习过程中&#xff0c;可能会遇到后端服务里有使用 dubbo 协议实现的接口&#xff0c;dubbo 协议接口的测试方法不同于 http/https 类型的接口&#xff0c;不能简单使用request.post的方法来完成自动化测试。 如果需要对 dubbo 协议的接口进行自动化测试&#…

FFmpeg进阶:生成视频的缩略图

文章目录1.读取对应位置的视频帧2.添加时间信息3.对图像进行拼接4.输出拼接图像5.显示效果很多时候为了方便预览视频内容&#xff0c;我们会随机的抽取视频当中的一些帧组成一个图片作为视频的缩略图。这里介绍一下如何通过FFmpeg生成视频的缩略图。其实原理很简单&#xff0c;…

MyBatis association解决多对一和collection解决一对多的映射关系

多对一的映射关系 创建Emp和Dept类 1.处理多对一映射关系方式一&#xff1a;级联属性赋值 2.处理多对一映射关系方式二&#xff1a;association实现 association:处理多对一的映射关系 property:需要处理多对的映射关系的属性名 javaType:该属性的类型 3.处理多对一映射关…

Metabase学习教程:视图-6

表格视图几乎可以来做所有的事情 了解如何设置条件格式、小条形图、值格式等。 表格是数据的自然栖息地&#xff0c;对应关系数据库列和对应的行记录。它们可能不像条形图或者地图&#xff0c;但当你在很多领域工作时&#xff0c;它们往往是你所需要的。Metabase中的表可视化…

运动品牌推荐:2022年最值得入手的一些运动装备

运动是一个比较枯燥的过程&#xff0c;不断的身体重复&#xff0c;会让运动者的注意力过度的关注到自己身体的疲惫感并且放大&#xff0c;这个时候我们就可以通过外在的运动装备来消除这些疲劳感&#xff0c;提高自己的运动积极性。不过哪些运动装备好用并适合自己呢&#xff1…

服务器配置怎么查看

服务器配置怎么查看 在我们找服务器商买服务器时&#xff0c;一般都是根据自己需求来选择需要什么配置的服务器。 选服务器时主要看CPU、内存、硬盘、带宽、这几个主要配置今天艾西就教你怎么查看服务器配置 CPU、内存怎么查看&#xff1a; 方法一&#xff1a;我们远程进入服…

学术Paper写作技巧要点讲解

在国外图书馆阅读他人的学术文章的时候&#xff0c;是否发现他们英文与你的不一样&#xff1f;虽然他们的Paper与你的有相似的结构&#xff0c;即开头、正文、结论&#xff0c;但是你的写作与他们的比起来还是显得简单多了。就是类似于国内毕业Paper的写作&#xff0c;在国外学…

断点续传小解

断点续传的原理 HTTP 协议是互联网上应用最广泛网络传输协议之一&#xff0c;它基于 TCP/IP 通信协议来传递数据。断点续传的奥秘就隐藏在这 HTTP 协议中了。 我们知道HTTP请求会有一个Request header 和 Response header&#xff0c;在请求头里边有个和Range相关的参数 当下…

6种交互式内容创意帮助跨境电商卖家提高独立站商店知名度

关键词&#xff1a;跨境电商卖家、独立站商店 交互式内容是一种允许用户与之交互的内容。一些示例包括在线投票、问答环节、交互式视频和交互式计算器等交互式工具。此内容类型允许查看者通过单击或拖动项目来自定义显示方式和内容。内容还可以引导读者采取您想要的操作&#x…

【网络安全】——sql注入之云锁bypass

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座右…

嵌入式分享合集110

一、功耗&#xff0c;成为芯片设计的头号问题 很明显&#xff0c;热量将成为半导体未来的限制因素。已经有很大一部分芯片在任何时候都是黑暗的&#xff0c;因为如果所有东西同时运行&#xff0c;所产生的热量将超过芯片和封装消散该能量的能力。如果我们现在开始考虑堆叠模具…

智能网卡的网络加速技术

2021年9月25日&#xff0c;由“科创中国”未来网络专业科技服务团指导&#xff0c;江苏省未来网络创新研究院、网络通信与安全紫金山实验室联合主办、SDNLAB社区承办的2021中国智能网卡研讨会中&#xff0c;多家机构谈到了智能网卡的网络加速实现&#xff0c;我们对此进行整理&…

金枪鱼群优化算法(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…