树状数组详解

概述

树状数组(Binary Indexed Tree,简称BIT),是一种数据结构,用于处理区间查询和更新问题。它是一种可以高效地在对数级别时间复杂度内进行单点更新和区间查询的数据结构。树状数组通常用于解决以下两类问题:

  1. 区间和查询:给定一个序列,查询序列中任意区间的和。
  2. 区间更新:给定一个序列,对序列中任意区间的值进行增加或减少。

问题引入

给定一个长度为n的数组,完成以下两种操作:

  • 更新:将第x个数加上k;
  • 查询:输出区间[x, y]内每个数的和。

我们很容易想到一种朴素做法,更新操作直接在原数组上操作,查询遍历一下即可,对应的时间复杂度分别为O(1)和O(n)。

当然,你也可能想到用前缀和数组来优化,这样的话更新操作的时间复杂度就是O(n),查询操作的复杂度为O(1)。

可以发现,两种做法中,要么查询是O(1),更新是O(n);要么更新是O(1),查询是O(n)。那么就有没有一种做法可以综合一下这两种朴素做法,然后整体时间复杂度可以降一个数量级呢?有的,对,就是树状数组。

lowbit函数

学习树状数组之前首先需要了解一下lowbit函数。lowbit函数的功能就是求某一个数的二进制表示中最低的一位1所表示的数值。这个数值一定是2的幂。举个例子,x = 6,它的二进制为110,那么lowbit(x)就返回2,因为最后一个1表示2。再举个例子,lowbit(4) = 4

我们知道,负数的补码是它的反码+1。当然,还有一种快捷求法就是,从右往左数第一个1及其右边的0不动,剩下的位取反。这时候,我们如果让它和原数进行二进制与操作,就能得到最后的一个1及其后面的0。例如,6的二进制为0110,-6的补码为1010,它们两个做与运算就能消掉最后一个1前面的所有位。用代码表示如下:

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

树状数组的思想

首先要明确树状数组里存的是什么。假设原数组是arr,我们需要维护一个新的树状数组cc数组里的每一位存的是arr中对应下标开始往前数lowbit(下标)个数的和。例如,c[6]的下标为6,并且lowbit(6) = 2,所以c[6]存的就是arr中从第6项开始往前数2个数的和,即arr[5] + arr[6]。因此,相比前缀和数组,树状数组可以说存的是区间和。

查询

明白了树状数组存的是什么,就可以用树状数组来求前缀和了。因为查询操作还是要通过两个前缀和做差来得到任意区间的和。

因为树状数组存的是区间和,我们通过不同的区间拼凑出一个完整的前缀区间就能计算前缀和。还是以6为例,6的二进制为110,可以写成100 + 10,即4 + 2。根据树状数组的定义,c[6]存的是arr[5] + arr[6]。得到第一个区间和后,减去lowbit,即6 - lowbit(6) = 6 - 2 = 4。而c[4]存的是arr中第1项到第4项的和,这是因为lowbit(4) = 4。这两段拼起来正好得到第6项的前缀和。

因此,用树状数组求第x项前缀和可以用下面的代码表示:

int sum(int x, int c[])
{int res = 0;for (; x > 0; x -= lowbit(x))res += c[x];return res;
}

更新

如果理解了上述过程,我们其实能发现,树状数组求前缀和本质上就是将下标展开成二进制,根据二进制位上的1来求和,从而实现对数级别的复杂度。树状数组用图来表示就是像下面这样。

其中,1到12是树状数组的下标,上面的横条表示了这一项对应arr数组中的区间和。我们从这张图中可以得到树状数组的如下性质:

  • 下面层的下标只要补上自己的lowbit值就可以得到上面层的下标(图中的虚线指出了什么是上面层)。注意,是上面层的下标,而不是上一层的下标,这个性质就是更新操作的依据;

例如,下标6只要加上lowbit(6),也就是2,就能跳到自己的上面层,也就是8。之所以8是上面层,是因为它们的区间产生了重叠。加上lowbit值会让最低位的1往高位移动,其所代表的幂会指数增长,远大于加上的值。所以上面层必定包含下面层。如果不能理解记住这个性质就好。

理解了这一点,就可以明白更新操作了。如果在arr数组上进行更新操作,很简单,只要修改第x项就可以了。但是树状数组表示的是区间和,修改了这一项会影响到很多包含这一项的区间。因此,在c数组上,所有包含第x项的区间和都要修改。

更新了arr的第x项,首先影响到的就是c[x]。因为c[x]所代表的区间和长度至少为1,即必定包含arr[x]。然后就是上面的性质所说的上面层了。我们通过不断加上lowbit值往上层跳,不断更新c数组,就能实现对数级别复杂度的更新操作。代码如下:

void update(int x, int val, int c[], int n)
{for (; x <= n; x += lowbit(x))c[x] += val;
}

代码实现

输入格式

第一行输入两个整数n和m,分别表示数组长度和操作的次数;

第二行输入n个整数表示数组;

接下来m行,每行输入一个字符ch和两个整数x,y。ch='F'表示查询x到y这段闭区间的和;ch='S'表示第x个元素加上y。

输出格式

对于每个查询,输出结果。

样例输入

5 6
1 2 3 4 5
F 1 3
S 1 2
F 1 3
S 2 3
F 1 2
F 1 5

样例输出

6
8
8
20

完整代码实现如下:

#include <iostream>using namespace std;const int MAX = 1e6;
int c[MAX]; // c[i]表示从第i个元素向前数lowbit(i)个元素,这一段的和,包括c[i]int lowbit(int x)
{return x & -x;
}/*** @brief 求下标为x的前缀和** @param x* @return int*/
int sum(int x, int c[])
{int res = 0;for (; x > 0; x -= lowbit(x))res += c[x];return res;
}/*** @brief 原数组x的位置上的数加上了val,所以要维护c数组** @param x* @param val* @param c* @param n*/
void update(int x, int val, int c[], int n)
{for (; x <= n; x += lowbit(x))c[x] += val;
}int main()
{int n, m;cin >> n >> m;for (int i = 1; i <= n; i++){int x;cin >> x;update(i, x, c, n);}while (m--){int x, y;char ch;cin >> ch >> x >> y;switch (ch){case 'F':cout << sum(y, c) - sum(x - 1, c) << endl;break;case 'S':update(x, y, c, n);break;}}return 0;
}

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

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

相关文章

freeswitch(开启支持MCU视频会议,使用mod_av模块)

亲测版本centos 7.9系统–》 freeswitch1.10.9 本人freeswitch安装路径(根据自己的路径进入) /usr/local/freeswitch/etc/freeswitch场景说明: 有些场景想使用视频会议MCU融合画面进行开会使用方法: 第一步:下载插件 yum install -y epel-release yum install

【大数据技术基础】【记录Ubuntu 16.04升级到18.04】Ubuntu的一个版本升级到另一个版本

在 Ubuntu 操作系统中进行软件更新和系统升级 Ubuntu Kylin 16.04 LTS 系统进行系统升级到 Ubuntu 18.04.6 LTS 版本 升级提示&#xff1a;系统弹出提示框&#xff0c;告知用户有新版本的 Ubuntu 可用&#xff0c;询问用户是否想要升级。 认证窗口&#xff1a;显示了一个认证…

这是一个vue3 + scss的数字滚动效果

介绍: 当数字变化时&#xff0c;只改变变化的数字位&#xff0c;其余的不变&#xff0c;可以递增、递减、骤变、负数也可以&#xff0c;但是样式要根据具体的项目需求去改&#xff1b; 效果1、增加数字&#xff1a; 效果2、减少数字&#xff1a; 使用方法&#xff1a; <te…

TortoiseGit的下载、安装和配置

一、TortoiseGit的简介 tortoiseGit是一个开放的git版本控制系统的源客户端&#xff0c;支持Winxp/vista/win7.该软件功能和git一样 不同的是&#xff1a;git是命令行操作模式&#xff0c;tortoiseGit界面化操作模式&#xff0c;不用记git相关命令就可以直接操作&#xff0c;读…

最新版Chrome浏览器加载ActiveX控件之Adobe PDF阅读器控件

背景 Adobe PDF阅读器控件是一个ActiveX控件&#xff0c;用于在Windows平台上显示和操作PDF文件。它提供了一系列方法和属性&#xff0c;可以实现对PDF文件的加载、显示、搜索、打印、保存等操作。 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件…

源码分析之Openlayers中的控件篇Control基类介绍

概述 Openlayers 中内置了9类控件&#xff0c;这9类控件都是基于Control类&#xff0c;而Control类则是继承于BaseObject类&#xff0c;如下图所示&#xff1a; 如上&#xff0c;这9类控件分别是&#xff1a; Attribution&#xff1a;属性控件FullScreen:全屏控件MousePositi…

第P2周:Pytorch实现CIFAR10彩色图片识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标 实现CIFAR-10的彩色图片识别实现比P1周更复杂一点的CNN网络 具体实现 &#xff08;一&#xff09;环境 语言环境&#xff1a;Python 3.10 编 译 器: …

2024年食堂采购系统源码技术趋势:如何开发智能的供应链管理APP

本篇文章&#xff0c;小编将与大家一同探讨2024年食堂采购系统的技术趋势&#xff0c;并提供开发更智能的供应链管理APP的策略。 一、2024年食堂采购系统的技术趋势 1.人工智能与机器学习的深度应用 在2024年&#xff0c;AI和机器学习在食堂采购系统中的应用将更加普遍。这些…

系统架构的演变

什么是系统架构&#xff1f; 系统架构是系统的一种整体的高层次的结构表示&#xff0c;它确定了系统的基本组织、组件之间的关系、组件与环境的关系&#xff0c;以及指导其设计和发展的原则。随着技术的发展和业务需求的增长&#xff0c;系统架构经历了从简单到复杂、从集中到…

【从零开始入门unity游戏开发之——C#篇04】栈(Stack)和堆(Heap),值类型和引用类型,以及特殊的引用类型string

文章目录 知识回顾一、栈&#xff08;Stack&#xff09;和堆&#xff08;Heap&#xff09;1、什么是栈和堆2、为什么要分栈和堆3、栈和堆的区别栈堆 4、总结 二、值类型和引用类型1、那么值类型和引用类型到底有什么区别呢&#xff1f;值类型引用类型 2、总结 三、特殊的引用类…

【C语言实现:用队列模拟栈与用栈模拟队列(LeetCode 225 232)】

LeetCode刷题记录 &#x1f310; 我的博客主页&#xff1a;iiiiiankor&#x1f3af; 如果你觉得我的内容对你有帮助&#xff0c;不妨点个赞&#x1f44d;、留个评论✍&#xff0c;或者收藏⭐&#xff0c;让我们一起进步&#xff01;&#x1f4dd; 专栏系列&#xff1a;LeetCode…

java jar包加密 jar-protect

介绍 java 本身是开放性极强的语言,代码也容易被反编译,没有语言层面的一些常规保护机制,jar包很容易被反编译和破解。 受classfinal&#xff08;已停止维护&#xff09;设计启发,针对springboot日常项目开发,重新编写安全可靠的jar包加壳加密技术,用于保护软件版权。 使用说…

Linux:Git

Git常见指令&#xff1a; git help xx_command git xx_command --help git --version 查看git版本git config --global user.name "xxx_name" 全局级别的签名设置&#xff0c;全局的放在本用 git config --global user.ema…

【WiFi】WiFi中RSSI、SNR、NF之间关系及说明

RSSI&#xff08;接收信号强度指示&#xff09; 定义&#xff1a; RSSI 是一个相对值&#xff0c;用于表示接收到的无线信号的强度。它通常由无线设备的硬件&#xff08;如无线网卡或无线芯片&#xff09;直接提供。 计算&#xff1a; RSSI 的计算通常是由设备的无线芯片完成的…

[ZMQ] -- ZMQ通信Protobuf数据结构 1

1、前言背景 工作需要域间实现zmq通信&#xff0c;刚开始需要比较简单的数据结构&#xff0c;比如两个bool&#xff0c;后面可能就需要传输比较大的数据&#xff0c;所以记录下实现流程&#xff0c;至于为啥选择proto数据结构去做大数据传输&#xff0c;可能是地平线也用这个&…

顺序表的使用,对数据的增删改查

主函数&#xff1a; 3.c #include "3.h"//头文件调用 SqlListptr sql_cerate()//创建顺序表函数 {SqlListptr ptr(SqlListptr)malloc(sizeof(SqlList));//在堆区申请连续的空间if(NULLptr){printf("创建失败\n");return NULL;//如果没有申请成功&#xff…

5G中的随机接入过程可以不用收RAR?

有朋友提到了一种不用接收RAR的RA过程&#xff0c;问这个是怎么回事。其实在刚刚写过的LTM cell switch篇章中就有提到&#xff0c;这里把所有相关的内容整理如下。 在RACH-less LTM场景&#xff0c;在进行LTM cell switch之前就要先知道target cell的TA信息&#xff0c;进而才…

git 导出某段时间修改的文件 windows

第一步&#xff1a;列出两次commitID之间的文件变动 git diff oldid newid --name-only// 例如 git diff 4a886c57a8b5611a2abcfcd120461c2e92f7029a HEAD --name-only 4a886c57a8b5611a2abcfcd120461c2e92f7029a 代表之前 HEAD 代表最新或者换成某次commitID 例如&#xf…

Qt 联合Halcon配置

文章目录 配置代码窗口绑定 配置 选择添加库 选择外部库 LIBS -LC:/Program Files/MVTec/HALCON-17.12-Progress/lib/x64-win64/ LIBS -lhalconcpp\-lhdevenginecpp\-lhalconINCLUDEPATH C:/Program Files/MVTec/HALCON-17.12-Progress/include DEPENDPATH C:/Program Fil…

new URL(`../assets/images/${name}`, import.meta.url).href

背景&#xff1a; 文章讲述了Vite框架中关于资源文件&#xff08;如图片&#xff09;在默认配置下&#xff0c;如何正确处理开发环境和打包后的不同引用方式。重点介绍了使用import.meta.url和new URL() 来动态获取并处理静态资源URL的方法&#xff0c;以及注意事项&#xff0…