Index Tree(树状数组)

news2024/11/18 21:29:57

1、引入

线段树解决的是 区间查询区间更新 的问题, O ( l o g n ) O(logn) O(logn) 复杂度。

人为规定:数组下标从 1 开始。

如果要计算数组某个范围 L 到 R 的累加和,那么可以准备一个前缀和数组 help,得到前缀和数组后,可以 O ( 1 ) O(1) O(1) 复杂度 快速求出 L 到 R 的累加和。

如原数组 arr = [5, 3, 2, 4, 9, 0, 1],则 help = [5, 8, 10, 14, 23, 23, 24],如果要计算下标 3 ~ 5 的值的累加和,直接用 help[5] - help[2] = 15,求解速度非常快。但是这种求解数组某个范围的累加和的方法是基于原数组 arr 不再变化的前提。

如果原数组中的某个数修改了值,那么 help 数组就存在需要大量更新的问题。

那么对于原数组中的值可能变化的情况,但是还能查询范围上的累加和,需要换个结构。即该结构同时满足以下两个方法:
1)update(i, v):将 i 位置的值修改为 v
2)sum[L...R]:计算 L 到 R 范围的累加和。

这个结构就是 Index Tree,上述的两个方法线段树可以完美实现,但是 Index Tree 有比线段树更优的地方。

Index Tree 没法像线段树一样做到范围更新,只能单点更新后执行范围累加和的查询,这两个方法都快。

2、Index Tree 的特点

  1. 支持区间查询
  2. 没有线段树那么强,但是非常容易改成一维、二维、三维的结构
  3. 只支持单点更新

3、Index Tree讲解

原始数组 arr:[31-23609]
	   下标: 1   2   3   4   5   6  7

准备一个 help 数组(相同长度的配成一对)
1)1位置的3长度为1,前面没有长度为1的可以与之组成一对,于是就拷贝自己的值 help[1] = arr[1] = 3 (长度为1)
2)2位置的1长度为1,前面有长度为1的3可以与之组成一对,于是 help[2] = arr[1] + arr[2] = 4 (长度为2)
3)3位置的-2长度为1,前面没有长度为1的可以与之组成一对,于是拷贝自己的值 help[3] = arr[3] = -2 (长度为1)
4)4位置的3长度为1,前面有长度为1的-2可以与之组成一对,二者相加 = 1,此时已经长度为2,而前面又有长度为2的可以与之组成一对,于是 help[4] = arr[1] + arr[2] + arr[3] + arr[4] = 5 (长度为4)
5)5位置的6长度为1,附近前面没有长度为1的,于是 help[5] = arr[5] = 6 (长度为1)
6)6位置的0长度为1,前面长度为1的6可以与之组成一对,help[6] = arr[5] + arr[6] = 6 (长度为2)
7)7位置的9长度为1,前面没有长度为1的可以与之组成一对,于是help[7] = arr[7] = 9 (长度为1)

抛开数组中的值,仅看 help 数组每个元素管理的原数组的下标:
请添加图片描述
相同长度进行合并

规律1 :help 数组的 index 管理的是 index 的二进制形式中的从右往左的第一个1拆开后加1到它自己这个范围的数

例如 index = 010111000,那么它管理的是原数组中的 010110001 ~ 010111000 这个范围的值,即将 index 中从右往左数的第一个1拆开后加1到它自己的范围。

例 原数组 arr 下标从 1 到 8,那么help数组中的 index = 8 的位置管理的是原数组1到8范围的值,即 8 = 01000,拆开1加1 = 00001,所以管理的范围为 00001 ~ 01000 就是 1~8 范围。

同理 index = 12时,12 = 01100,第一个1去掉后加1 = 01001,所以管理的范围是 01001 ~ 01100,即原数组的9 ~ 12范围。

利用help数组求原数组中1 ~ i i i 位置的前缀和

例1:求原数组1 ~ 33 范围的前缀和,33 的二进制形式是0100001,那么就是help数组中 33 这个位置的值 a,以及将33的二进制形式的最后一个1去掉得到的0100000,对应到help数组中的该位置的 b,将help数组中的这两个位置的值相加即是原数组 1 ~ 33 这个范围的前缀和。为什么正确呢?因为help数组中的 33 就管理原数组的33位置的数,而help数组中的32 位置管理1 ~ 32 位置的数,所以二者相加就是1 ~ 33 范围的和。

例2:求原数组 1 ~ i i i 范围的前缀和,其中 i = 0101100110 i = 0101100110 i=0101100110,那么就是取出 help 数组的如下位置的值进行累加:

help[0101100110] = a,拿出help数组中 $i$ 位置的数
help[0101100100] = b,在上一步的基础上抹掉一个1
help[0101100000] = c,在上一步的基础上抹掉一个1
help[0101000000] = d,在上一步的基础上抹掉一个1
help[0100000000] = e,在上一步的基础上抹掉一个1
直到没有 1 可以抹掉,即index = 0 为止。

所以 sum[1...i] = a + b + c + d + e

为什么这个流程正确呢?

i = 0110100 i = 0110100 i=0110100 时,在上述流程中取的是 help 数组中的:

help[0110100],管理的是原数组的 arr[0110001] ~ arr[0110100]

help[0110000],管理的是原数组的 arr[0100001] ~ arr[0110000]

help[0100000],管理的是原数组的 arr[0000001] ~ arr[0100000]

可见,管理的原数组的范围全部是连续的,所以就正确得到了原数组 1 ~ i i i 的前缀和。

4、代码实现

public class IndexTree {

	// 下标从1开始!
	public static class IndexTree {

		private int[] tree;
		private int N;

		// 0位置弃而不用!
		public IndexTree(int size) {
			N = size;
			tree = new int[N + 1];
		}
		// 根据index的位信息处理的,index的二进制位中有多少个1就需要操作多少次,得到每位的信息就是每次右移1位,即除以2
		// 所以时间复杂度 O(logn)
		// 函数功能:求1~index范围的累加和
		// 所以如果要求 L 到 R的累加和 = 1~R的累加和 - 1~L-1的累加和
		public int sum(int index) { 
			int ret = 0;
			while (index > 0) {
				ret += tree[index];
				index -= index & -index; //去掉最右侧的1
			}
			return ret;
		}

		// index & -index : 提取出index最右侧的1出来
		// index :           0011001000
		// index & -index :  0000001000
		// x & (-x) = x & (~x + 1) 
		public void add(int index, int d) {  //时间复杂度 O(logn)
			//认为原始数组一开始全部为0,现在要给index位置的数加d,那么tree数组(即help数组)就会有位置的数受到影响
			//比如原数组的 3 位置的数发生了变化,那么 tree 数组中的哪些位置会受牵连呢?
			// 3 的二进制为011,将最右侧的1加1 = 100,即4
			// 4 = 100 最右侧的1 加1 = 1000,即8
			// 8 = 1000 最右侧的1 加1 = 10000,即16
			// 即 011 -> 100 -> 1000 -> 10000 ->.... 
			//规律:只需要将发生更新位置的值的二进制的最右侧1加1 就能得到它影响tree数组哪些位置的值
			while (index <= N) {
				tree[index] += d; //当前位置+d
				index += index & -index; //最右侧的1加1,找出受到牵连的位置
			}
		}
	}
	
	//暴力解
	public static class Right {
		private int[] nums;
		private int N;

		public Right(int size) {
			N = size + 1;
			nums = new int[N + 1];
		}

		public int sum(int index) {
			int ret = 0;
			for (int i = 1; i <= index; i++) {
				ret += nums[i];
			}
			return ret;
		}

		public void add(int index, int d) {
			nums[index] += d;
		}

	}

	public static void main(String[] args) {
		int N = 100;
		int V = 100;
		int testTime = 2000000;
		IndexTree tree = new IndexTree(N);
		Right test = new Right(N);
		System.out.println("test begin");
		for (int i = 0; i < testTime; i++) {
			int index = (int) (Math.random() * N) + 1;
			if (Math.random() <= 0.5) {
				int add = (int) (Math.random() * V);
				tree.add(index, add);
				test.add(index, add);
			} else {
				if (tree.sum(index) != test.sum(index)) {
					System.out.println("Oops!");
				}
			}
		}
		System.out.println("test finish");
	}
}

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

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

相关文章

【寒假每日一题】洛谷 P6263 [COCI2014-2015#3] STROJOPIS

题目链接&#xff1a;P6263 [COCI2014-2015#3] STROJOPIS - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 正确的打字正成为文化的重要组成部分。如果你仍然没有使用所有的十根手指来打字&#xff0c;你必须重新学习打字——然后你会打字更快&#xff0c;感觉更舒适…

Web进阶:Day1 字体图标、平面转换、空间转换、动画

Web进阶&#xff1a;Day1 Date: October 3, 2022 Summary: 字体图标、平面转换、空间转换、动画 字体图标 字体图标 字体图标展示的是图标&#xff0c;本质是字体 处理简单的、颜色单一的图片 注&#xff1a;复杂的用CSS精灵&#xff0c;简单的用字体图标 字体图标的优点&…

Verilog语法笔记(夏宇闻第三版)-条件语句

目录 if_else语句&#xff1a; 五点说明&#xff1a; case语句&#xff1a; 真值表&#xff1a; ​例&#xff1a; ​由于使用条件语句不当在设计中生成了原本没想到有的锁存器&#xff1a; if_else语句&#xff1a; if语句是用来判定所给定的条件是否满足&#xff0c;…

(十四)面向对象的三大特征

目录 前言: 一、面向对象三大特征之一:封装 二、面向对象三大特征之二:继承 三、面向对象三大特征之三:多态 前言: 面向对象的三大特征:封装、继承、多态。 一、面向对象三大特征之一:封装 1.概述: 封装是把过程和数据包围起来&#xff0c;对数据的访问只能通过已定义的接口…

使用Idea编码常用的28种技巧方式

一丶列表: 1丶查看代码历史版本 2丶调整idea的虚拟内存&#xff1a; 3丶idea设置成eclipse的快捷键 4丶设置提示词忽略大小写 5丶关闭代码检查 6丶设置文档注释模板 7丶显示方法分隔符 8丶设置多行tab 9丶快速匹配方法的大括号位置 10丶代码结尾补全 11丶模糊搜索方法 12丶预览…

用 Python selenium爬取股票新闻并存入mysql数据库中带翻页功能demo可下载

用 Python selenium爬取实时股票新闻并存入mysql数据库中1.分析需求2.创建表3.分析需要爬取的网页内容4.python里面selenium进行爬虫操作1.添加包2.连接数据库3.selenium爬虫前配置4.对股票新闻内容爬取并存入mysql中5.翻页功能6.运行程序首先我们先明确我们的主要目标就是要爬…

5、SySeVR复现——Data preprocess(上)

目录 1、环境 2、生成切片对应的hash 3、获取要删除的切片位置信息 4、对切片进行token化 1、环境 从数据预处理开始&#xff0c;操作系统&#xff1a;windows 10 &#xff0c;软件&#xff1a;pycharm 注&#xff1a;对官方提供的文件&#xff0c;做了一些改动&#xff0c…

插槽 slot

文章目录一、什么是插槽二、插槽内容三、渲染作用域四、默认内容五、具名插槽六、作用域插槽一、什么是插槽 我们使用 <slot> 作为一个占位符&#xff0c;父组件就可以把要传递的内容显示到占位符所在位置上&#xff0c;提高组件使用的灵活性。 二、插槽内容 父组件向…

安信可VC系列语音识别的使用教程

安信可VC-02语音识别的应用&#xff0c;本篇只讲述在Windows系统下的应用。Linux下的请参考官方文档介绍和说明。 1-安信可VC-02离线语音识别简介 VC系列模组是我司开发的一款AI离线语音识别的产品&#xff0c;主芯片是云知声推出的离线语音识别芯片锋鸟M(US516P6)&#xff0c…

Redis基础语法和SpringBoot集成使用

在初期&#xff0c;已经讲述了Redis安装问题。现在正式进入Redis的入门阶段。 Redis客户端 命令行客户端 redis-cli [options] [commands]常用到的 options 有&#xff1a; -h 127.0.0.1: 指定要连接的Redis的IP地址【默认127.0.0.1】-p 6379: 指定连接Redis的端口【默认63…

jenkins前端页面自动运维值yarn编译运行项目

配置步骤如下 首先需要在系统管理中心安装node相关插件 安装完成之后&#xff0c;在系统管理——>全局工具配置——>NodeJS 点击新增Nodejs 此处自定义别名&#xff0c;我这里是Nodejs16&#xff0c;取消自动安装前面的复选框&#xff0c;下方选择我们的nodejs安装目录&…

云服务器定时执行python脚本

文章目录前言crontab简介基本语法定时任务具体内容python 脚本定时任务前言 在服务器上定时执行任务有两种方式&#xff0c;一种是at定时任务&#xff0c;一种是crond任务调度&#xff0c;at命令是一次性定时计划任务&#xff0c;at的守护进程 atd会以后台模式运行&#xff0c…

Spring注解之@validated使用

概念 spring-boot中可以用validated来校验数据&#xff0c;如果数据异常则会统一抛出异常&#xff0c;方便异常中心统一处理。 注解源码&#xff1a; Validated 作用在类、方法和参数上 Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER}) Retention(…

python初级教程十一 urllib

urllib Python urllib 库用于操作网页 URL&#xff0c;并对网页的内容进行抓取处理。 本文主要介绍 Python3 的 urllib。 urllib 包 包含以下几个模块&#xff1a; urllib.request - 打开和读取 URL。 urllib.error - 包含 urllib.request 抛出的异常。 urllib.parse - 解…

【Python入门指北】操作数据库

文章目录一、1.数据库2.练手案例二、redis数据库一、 1.创建一个数据库 [guanmaster1 ~]$ mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL…

Apache IoTDB PMC 乔嘉林荣获 2022 杰出开源贡献者|开源技术强大,开源文化活跃...

2022 年 12 月 29 日至 30 日&#xff0c;2022 木兰峰会正式召开&#xff0c;会上发布了中国开源云联盟 2022 年度评选名单。本次评审专家包括数十位开源领域专家、社区领袖、科研院所专家&#xff0c;共评选出杰出开源贡献者 3 人。其中&#xff0c;清华大学助理研究员、博士后…

【QT开发笔记-基础篇】| 第五章 绘图QPainter | 5.16 完结和后续:《Qt开发专题-自定义控件》

本节对应的视频讲解&#xff1a;B_站_视_频 https://www.bilibili.com/video/BV1NW4y1K7eL 1. 为什么需要自定义控件 绘图最大的一个应用场景就是自定义控件&#xff0c;Qt 本身提供的一些控件是有限的&#xff0c;并且它提供的一些控件很可能不满足我们的需要 这种情况下&a…

pygame - 图片移动优化

目录 一、优化过程分析 1、pygame - 图片移动中图片移动模式 2、优化过程 二、代码段 1、附注释 2、无注释 三、效果展示 一、优化过程分析 1、pygame - 图片移动中图片移动模式 按一次方向键&#xff0c;图片移动一小步&#xff1b; 若需要一直往某个方向移动&…

【LaTex】LaTex 极简安装教程

文章目录Latex 安装教程1. 下载texlive.iso2. 点击装载3. 运行 install-tl-windows.bat 进行安装4. 验证是否安装成功5. 安装开发工具Latex 安装教程 1. 下载texlive.iso https://mirrors.tuna.tsinghua.edu.cn/ctan/systems/texlive/Images/ 2. 点击装载 下载完成后&#x…

【博客580】内核如何决定数据包的源ip

内核如何决定数据包的源ip 1、Traversing of packets Receive&#xff1a; 某个interface收到数据包 -> PREROUTING (manage, nat) -> routing -> 是发送给本机的数据包? -> INPUT (manage, filter) -> app 不是 -> FORWARD (manage, filter) -> POST…