数据结构:树状数组详解

news2025/1/8 11:49:57

一. 背景

   那么我们为什么要用树状数组呢? 

在解决一些区间求和的问题中 , 简单描述就是,对于一个给定的数组A,希望能够设计一个update函数来修改其中一个数的值,然后再设计一个sum函数来计算数组下标再给定参数l和r之间的值之和。关键点在于这两个函数可能被无数次调用,所以需要保证两个函数的复杂度都要小。

平时我们对这类问题常用的两种方法是 : 

1>暴力算法
update 函数采用对数组直接修改的方式。 O(1) ,     对于sum 函数,返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的和,但是这道题的操作数很大,会超时。 O(n)
2>前缀和算法
sum函数,考虑前缀和的方法,也就是采用动态规划的方法,设计一个新的数组sums来表示前缀和。
其基本思想为, sums [i]表示A[0]+A[1]+A[2]+...+A[i-1]。具体操作的时候我们只需要设置sums[i] = sums[i-1]+A[i-1]即可。
如果希望计算数组下标从l到r的区间和,只需要计算sums[r+1]-sums[l]即可。希望使用这种方式达到简化计算复杂度的目的。 但是,这时候需要考虑update函数,当我们对数组A直接进行修改值之后,我们发现sums数组也需要进行修改。
举个栗子:
原数组:A= {1,2,3,4,5}
前缀和数组:sums= {0,1,3,6,10,15}
分析:当我们对A[2]进行修改,将其原值3改为2的时候。
这样一来,对于update函数,我们的复杂度就会提高,可以认为是O(n),即使当前sum函数复杂度变为了O(1),这道题总的复杂度依然是O(n)。
所以说 , 遇到这类问题 首先需要能够快速计算区间和,其次要保证在修改了数组的值之后,对相关数据结构内容修改的操作数也要尽量少,  从而就引出了树状数组.

二.树状数组

树状数组是一个查询和修改复杂度都为log(n)的数据结构。主要用于数组快速单点修改和快速区间求和.
图解如下 :

       最底下一行绿色结点从左到右为原数组A,而带有数字的黑色结点就是树状数组C的结点 ,我们可以看到比如说C[8]结点,它的子结点有C[4],C[6],C[7],A[7](第八个绿色结点)而C[4],C[6],C[7]由分别有自己的子结点。 根据这张图,对于C[8]结点可以表示成:

C[8] = C[4] + C[6] +C[7] +A[7]
换一张图来理解一下:
        这张图里表示的所有数字结点就是树状数组C,最下面一行从左到右同时也表示原数组A(包括空结点)。图中标出了修改原数组中第5个元素需要修改的所有结点值的更新过程和计算原数组中下标0~14的结点值之和的查询过程。
<1> 更新过程
        根据上面树状数组的计算,我们参考这张图能够知道,C[5]是C[6]的子结点,C[6]是C[8]的子结点,而C[8]是C[16]的子结点,当我们修改A[4]的时候因为C[5] = A[4],所以C[5]会变化,导致C[6]变化,再导致C[8]变化,再导致C[16]变化,那么变化的值为多少呢,均为 A[4]新值减去A[4]旧值.
这里的思想和之前提到的前缀和有点相似,前缀和中是将sums[5]及后面所有的结点都要修改(因为这些结点都包含了A[4]结点的信息),但是这里只需要修改 C[5],C[6],C[8]和C[16]结点的信息(因为只有这几个结点包含了A[4]结点的信息),总体来说比起前缀和方法更新结点的时候对数据结构的更新就很快了.
那这是画图看出来要更新C[5[,C[6],C[8]和C[16]。那么具体是修改哪些结点呢?这里我们要从二进制的角度来看,重新画一张二进制的图,将所有结点上的十进制改为了二进制表示:

对应过来,我们会发现C[101]的值变化后,会影响C[110],进而影响C[1000]的值那么101和110和1000之间有什么关系呢?
可以归纳得出,110是由101加上1得到,1000是由110加上10得到,10000是由1000加上1000得到的
这里我们设计一个函数lowbit(int x)用于计算给定一个下标x,返回其二进制下标只保留最低位1会得到的那个数 , 也就是110为什么可以到101的一个算法 :
以x=5为例:
public int lowbit(int x){
        return x & (-x);
    }
正数的原码、反码、补码一样
示例: 5
原码: 0000 0101
反码:0000 0101
补码:0000 0101
负数的原码、反码、补码 (第一位表示符号位 1:负数 0:正数)
示例: -5
原码: 1000 0101
反码:1111 1010(将原码除符号位外其它位按位取反)
补码:1111 1011 (反码+1)
那么-5也就是  1111  1011
7 & -7 = 0000  0001 = 2
所以我们在更改完tr[5] 之后下一个要更改的是  5+2 = 7,也就是tr[7]
那么在A[4](C[101])的值变了之后,我们只需要对101计算lowbit得到1,然后修改C[110]的值,然后计算110的lowbit得到10,再修改C[1000]的值,这样不断反复,直到当前需要修改的节点下标越界即可。这样我们就得到了我们的update函数:
 //更新操作
    public void update(int index,int val){
        for (int i = index; i < this.tr.length; i += lowbit(index)) {
            tr[i] += val;
        }
    }
整个复杂度为O(logn),因为每次lowbit导致移了一位,相当于反复对数组长度n除以2直到0.
<2>查询过程
        查询过程是计算从A[0]到A[i]的所有值进行求和 ,  如果我需要查询A[0]~A[14]之和,观察当前图我们会发现 8结点包含了0~7,12结点包含了8~11,14结点包含了12~13,15结点包含了14
那我们只需要将C[8],C[12],C[14],C[15]进行求和即可
同样的也是用到lowbit函数
//求和
    public int sumRange(int index){
        int sum = 0;
        for (int i = index; i > 0; i-=lowbit(index)) {
            sum += this.tr[i];
        }
        return sum;
    }
查询过程相当于是求前缀和的过程,那么有了前缀和,我们就可以通过 前缀和作差得到其中部分区间和的答案 了。
这里使用到的复杂度为O(logn),因为每次lowbit移了一位,相当于反复对数组长度n除以2直到0的复杂度。

 

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

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

相关文章

Docker Swarm 集群搭建和使用 —— 筑梦之路

简单介绍 swarm 集群由管理节点&#xff08;Manager&#xff09;和工作节点&#xff08;Worker&#xff09;构成。 管理节点&#xff1a;主要负责整个集群的管理工作包括集群配置、服务管理等所有跟集群有关的工作。诸如监控集群状态、分发任务至工作节点等操作。 工作节点&…

【计算机视觉】使用 notebook 展示如何下载和运行 CLIP models,计算图片和文本相似度,实现 zero-shot 图片分类

文章目录 一、CLIP 模型二、准备三、加载模型四、查看图片处理器五、文本分词六、输入图片和文本&#xff0c;并可视化七、将图片和文字 encode 生成特征八、计算 cosine 相似度九、零样本进行图片分类十、编写函数进行图片分类十一、测试自己的函数十二、编写函数对多图片进行…

面对职业发展“迷茫期”除了抱怨焦虑我们还能做什么?

关注“软件测试藏经阁”微信公众号&#xff0c;回复暗号【软件测试】&#xff0c;即可获取氪肝整理的全套测试资源 Java和Python做自动化测试&#xff0c;哪个更有优势&#xff1f;这两个语言都是很流行的语言&#xff0c;所以从技术上很难说谁好谁不好的。因为要说好不好得看…

linux安装homeassistant(智能设备远程控制开源框架)

1、安装docker 先切换到root 用户&#xff0c;先安装一些基本环境&#xff1a; yum install -y yum-utils device-mapper-persistent-data lvm2添加阿里云软件源 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo然后安装 D…

QT+OpenGL高级光照 Blinn-Phong和Gamma校正

QTOpenGL高级光照1 本篇完整工程见gitee:QtOpenGL 对应点的tag&#xff0c;由turbolove提供技术支持&#xff0c;您可以关注博主或者私信博主 Blinn-Phong 冯氏光照&#xff1a;视线与反射方向之间的夹角不小于90度&#xff0c;镜面光分量会变成0.0&#xff08;不是很合理&am…

死信队列小结

死信队列是RabbitMQ中非常重要的一个特性。简单理解&#xff0c;他是RabbitMQ对于未能正常消费的消息进行的 一种补救机制。死信队列也是一个普通的队列&#xff0c;同样可以在队列上声明消费者&#xff0c;继续对消息进行消费处理。 对于死信队列&#xff0c;在RabbitMQ中主要…

Spring 是什么?IoC 和 DI的区别

1. Spring 是什么?2. IoC是什么&#xff1f; 2.DI概念说明 1. Spring 是什么? 我们通常讲的Spring指的是Spring Framework(Spring框架),它是一个开源的框架,有着活跃而庞大的社区,这也是它之所谓经久不衰的原因。官方的解读是:Spring官网 翻译过来就是:Spring使Java编程对每…

学会这5个步骤,就能轻轻松松地获取代码覆盖率报告

目录 前言&#xff1a; 1、创建main函数的test文件 2、插桩方式编译源码 3、运行主服务 4、执行测试用例 5、优雅退出主服务&#xff0c;并生成覆盖率报告 前言&#xff1a; 代码覆盖率报告可以帮助我们了解测试用例的质量和覆盖程度。 小编前期所测项目多为go语言研发&…

《C++高级编程》读书笔记(一:C++和标准库速成)

1、参考引用 C高级编程&#xff08;第4版&#xff0c;C17标准&#xff09;马克葛瑞格尔 2、建议先看《21天学通C》 这本书入门&#xff0c;笔记链接如下 21天学通C读书笔记&#xff08;文章链接汇总&#xff09; 1. C 基础知识 1.1 小程序 “hello world” // helloworld.cpp…

开源项目合集......

likeshop开源商城系统&#xff0c;公众号商城、H5商城、微信小程序商城、抖音小程序商城、字节小程序商城、头条小程序商城、安卓App商城、苹果App商城代码全开源&#xff0c;免费商用。 适用场景&#xff1a;B2C商城、新零售商城、社交电商商城、分销系统商城、小程序商城、商…

循环链表的创建

循环链表的介绍及创建&#xff08;C语言代码实现&#xff09; 点击打开在线编译器&#xff0c;边学边练 循环链表概念 对于单链表以及双向链表&#xff0c;其就像一个小巷&#xff0c;无论怎么样最终都能从一端走到另一端&#xff0c;然而循环链表则像一个有传送门的小巷&…

力扣 912. 排序数组

文章目录 一、题目描述二、题解1.快速排序2.堆排序3.二路归并排序 一、题目描述 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 示例 1&#xff1a; 输入&#xff1a;nums [5,2,3,1] 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;nums [5,1,1…

精细消费 年轻人和父母的奇妙交汇

日本社会学家三浦展结合对日本“311”大地震后的社会观察&#xff0c;提出了“第四消费时代”&#xff0c;即人们在经历了消费社会充分的发展过程之后&#xff0c;社会上逐渐兴起了低欲望、乐于共享、重视环保的消费理念。 在当时&#xff0c;主流观点普遍认为中国还处于大众化…

JWT单点登录

单点登录 文章目录 单点登录零、用户模块内容以及设计一、问题的提出二、单点登录SSO1.1 什么是单点登录1.2 单点登录的技术实现机制 二、远程调用方式RPC三、JWT的使用3.1 session的使用原理3.2 JWT介绍3.3 JWT原理3.4 JWT的使用 四、CAS实现单点登录的原理四、CAS的安装和代码…

十二、进程间通信

目录 目录 零、前置知识 一、什么是进程间通信 &#xff08;一&#xff09;含义 &#xff08;二&#xff09;发展 &#xff08;三&#xff09;类型 1.管道 2.System V IPC 3.POSIX IPC 二、为什么要有进程间通信 三、怎么进行进程间通信 &#xff08;一&#xff09;…

Snipaste工具推荐

Snipaste Snipaste 不只是截图&#xff0c;善用贴图功能将帮助你提升工作效率&#xff01; 新用户&#xff1f; 截图默认为 F1&#xff0c;贴图为 F3&#xff0c;然后请对照着 快捷键列表 按一遍&#xff0c;体会它们的用法&#xff0c;就入门啦&#xff01; 遇到了麻烦&…

Java通过Ip2region实现IP定位

我们在一些短视频平台上可以看到,视频作者或评论区可以显示IP地址,这其实就是根据IP获取到的我们可以通过一些在线网站就可以看到我们当前的公网IP和IP定位,最近有个需求也需要通过请求获取客户端的IP和IP的定位,于是通过一系列的百度,最终选择使用Ip2region这个工具库来进行定…

flutter的自定义系列雷达图

自定义是flutter进阶中不可缺少的ui层知识点&#xff0c;这里我们来总结下&#xff1a; 在Flutter中&#xff0c;自定义绘制通常是通过使用CustomPaint和CustomPainter来实现的。 创建CustomPaint组件 首先&#xff0c;需要创建一个CustomPaint组件。CustomPaint是一个Widge…

MobPush 厂商通道申请指南

华为厂商申请 创建应用 登录华为开发者联盟&#xff0c;注册您的应用&#xff0c;在应用信息中获取APP ID和Client Secret 配置SHA256证书指纹 在华为开发者联盟配置SHA256证书指纹。获取及配置请参见华为官方文档配置AppGallery Connect 设置消息回执 集成华为厂商通道SDK…

带你了解二进制

目录 视频参考&#xff1a; 讲解&#xff1a;​编辑 运算&#xff1a; 1001&#xff08;二进制&#xff09; 9&#xff08;十位数&#xff09;1111&#xff08;二进制&#xff09; 15&#xff08;十位数&#xff09;11001&#xff08;二进制&#xff09; 25&#xff08;…