树状数组详解

概述

树状数组(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中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件…

linux在没网的情况下如何校验时间 超详细拿来即用

一、没有校时服务器的话 1、手动修改 sudo date --set"2024-06-17 13:44:00"二、有校时服务器的话 1、手动校时 ntpdate 14.193.73.22、自动校时 写一个校时服务脚本 14.193.73.2 是校验时间服务器 #!/bin/sh while true dontpdate 14.193.73.2sleep 5;hwclock…

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

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

计算机网络知识点全梳理(二.HTTP知识点总结)

目录 HTTP基本概念 HTTP优缺点 HTTP优点&#xff08;1.1&#xff09; HTTP缺点 HTTP与HTTPS HTTP 与 HTTPS 的区别 HTTPS 解决 HTTP 的哪些安全问题&#xff1f; HTTPS 如何解决安全问题&#xff1f; HTTPS 连接建立的过程&#xff1a; HTTP/1.1、HTTP/2、HTTP/3 演…

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

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

Quant connect的优势和不足,学习曲线难

Quant connect的优势和不足 Quant connect作为一个成熟的算法交易平台&#xff0c;具有许多优势&#xff0c;包括&#xff1a; 强大的回测功能&#xff1a;Quant connect提供了丰富的数据源和回测功能&#xff0c;可以对各种交易策略进行全面的回测和分析。 容易上手&#xf…

深入理解 Ansible Playbook:组件与实战

目录 1 playbook介绍 2 YAML语言 2.1语法简介 2.2数据类型 3 Playbook核心组件 3.1 hosts组件 3.2 remote_user组件 3.3 task列表和action组件 3.4 handlers 3.5 tags组件 3.6 其他组件说明 1 playbook介绍 playbook 剧本是由一个或多个"play"组成的列表。…

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

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

代码随想录-算法训练营-番外(图论01:图论理论基础,所有可到达的路径)

day01 图论part01 今日任务:图论理论基础/所有可到达的路径 代码随想录图论视频部分还没更新 https://programmercarl.com/kamacoder/图论理论基础.html#图的基本概念 day01 所有可达路径 邻接矩阵 import java.util.Scanner;import java.util.List;import java.util.ArrayL…

系统架构的演变

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

c++总复习

C 中多态性在实际项目中的应用场景 图形绘制系统 描述&#xff1a;在一个图形绘制软件中&#xff0c;可能有多种图形&#xff0c;如圆形、矩形、三角形等。这些图形都有一个共同的操作&#xff0c;比如绘制&#xff08;draw&#xff09;。通过多态性&#xff0c;可以定义一个基…

pip离线安装一个github仓库

要使用pip安装一个本地Git仓库&#xff0c;你可以按照以下步骤操作&#xff1a; 确保你已经克隆了Git仓库到本地。 进入仓库所在的目录。 使用pip安装。 以下是具体的命令&#xff1a; 克隆Git仓库到本地&#xff08;替换下面的URL为你的仓库URL&#xff09; git clone https…

【从零开始入门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…

【Python】Selenium 爬虫的使用技巧和案例

引言 Selenium 是 Python 中功能强大的自动化测试工具,因其能够操控浏览器进行模拟操作,被广泛应用于网页数据爬取。相比传统的 requests 等库,Selenium 能更好地应对动态加载内容和复杂交互场景。本文将详细介绍 Selenium 爬虫的使用技巧,并提供实际案例来帮助读者快速上…

MySQL SQL语句性能优化

MySQL SQL语句性能优化指南 一、查询设计优化1. 避免 SELECT *2. 使用 WHERE 进行条件过滤3. 避免在索引列上使用函数和表达式4. 使用 LIMIT 限制返回行数5. 避免使用子查询6. 优化 JOIN 操作7. 避免全表扫描 二、索引优化1. 使用合适的索引2. 覆盖索引3. 索引选择性4. 多列索引…