数据结构: 树状数组

news2024/9/22 1:14:45

在OI赛事中,数据结构是非常重要的一个内容,更是有人说过,算法+数据结构=程序:
A l g o r i t h m + D a t a Algorithm+Data Algorithm+Data S t r u c t u r e = P r o g r a m m i n g Structure=Programming Structure=Programming
接下来,我们就来介绍一些经典重要的数据结构。
首先是树状数组。

实际上,数据结构就相当于一种算法,是在某种结构上实现某算法。
树状数组是一个查询和修改复杂度都为 O ( l o g 2 n ) O(log_2 n) O(log2n) 的数据结构,运用树状数组可以实现很多算法问题,

比如单点修改,区间求和,求逆序对等等。
比如区间求和,若 n n n 为数列长度, m m m 为查询次数,那么普通查询总和的最坏时间复杂度为 O ( n m ) O(nm) O(nm),假设 n n n m m m < = <= <= 1 0 5 10^{5} 105,时间复杂度就接受不了了,所以很多数据结构比如树状数组,RMQ问题的ST,线段树以及倍增(不太算是数据结构,偏向于 A l g o r i t h m Algorithm Algorithm)之类的,多数情况下都是为了压缩时间复杂度,尽量达到 O ( 1 0 5 − 1 0 7 ) O(10^5-10^7) O(105107)左右,使程序能在 1 s 1s 1s 的时间内求解。
而树状数组的代码效率远远高于线段树,树状数组代码比较少,但线段树代码量特别大

基本思想:

根据任意正整数关于 2 2 2 的不重复次幂的唯一分解性质,若一个正整数 x x x 的二进制表示为 1010 1 ( 2 ) 10101_{(2)} 10101(2),其中 1 1 1 的位是 0 , 2 , 4 0,2,4 0,2,4 ,则 x x x 可以被“二进制分解”成 2 4 + 2 2 + 2 0 2^{4}+2^{2}+2^{0} 24+22+20,进一步,区间 [ 1 , x ] [1,x] [1,x],可以分成 l o g 2 x log_2 x log2x个小区间:

  1. 长度为 2 4 2^4 24 的小区间 [ 1 , 2 4 ] [1,2^{4}] [1,24]
  2. 长度为 2 2 2^2 22 的小区间 [ 2 4 + 1 , 2 4 + 2 2 ] [2^{4}+1,2^{4}+2^{2}] [24+1,24+22]
  3. 长度为 1 1 1 的小区间 [ 2 4 + 2 2 + 1 , 2 4 + 2 2 + 1 ] [2^{4}+2^{2}+1,2^{4}+2^{2}+1] [24+22+1,24+22+1]

树状数组就是基于上述思想的数据结构,基本作用是维护序列的前缀和。
对于区间 [ 1 , x ] [1,x] [1,x],树状数组将其分为 l o g 2 x log_2 x log2x 个小区间, l o g 2 x log_2 x log2x 个子区间的累加和就是 [ 1 , x ] [1,x] [1,x] 的区间和。

基本算法:

由上文可知,这些子区间的共同特点是,若区间结尾为 R R R,则区间长度就等于 R R R 的“二进制分解”下最小的 2 2 2 的次幂,我们设为 l o w b i t ( R ) lowbit(R) lowbit(R)
l o w b i t lowbit lowbit的计算很简单, l o w b i t ( n ) = n lowbit(n)=n lowbit(n)=n & ( − n ) (-n) (n),表示 n n n在二进制表示下最低位的 1 1 1 以及后面的 0 0 0 构成的数。如 l o w b i t ( 10110 0 ( 2 ) ) = 10 0 ( 2 ) = 4 ( 10 ) lowbit(101100_{(2)})=100_{(2)}=4_{(10)} lowbit(101100(2))=100(2)=4(10)
我们就可以写一个子程序函数 l o w b i t lowbit lowbit 来实现:

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

对于序列 A A A,我们建立一个数组 C C C,其中 C C C [ x ] [x] [x] 保存序列 A A A 的区间 [ x − l o w b i t ( x ) + 1 , x ] [x-lowbit(x)+1,x] [xlowbit(x)+1,x] 中所有数之和。
树状数组

我们构造上图所示的结构,满足以下性质:

  1. C C C [ x ] [x] [x] 等于以 x x x 为根的子树中所有叶节点的和。
  2. C C C [ x ] [x] [x] 的叶节点个数等于 l o w b i t ( x ) lowbit(x) lowbit(x)
  3. C C C [ x ] [x] [x] 的父亲节点是 C C C [ x + l o w b i t ( x ) ] [x+lowbit(x)] [x+lowbit(x)]
  4. 深度为 l o g 2 N log_2 N log2N
    如图我们可以知道:
    C C C [ 1 ] [1] [1] = A [ 1 ] A[1] A[1]
    C C C [ 2 ] [2] [2] = A [ 1 ] A[1] A[1] + A [ 2 ] A[2] A[2]
    C C C [ 3 ] [3] [3] = A [ 3 ] A[3] A[3]
    C C C [ 4 ] [4] [4] = A [ 1 ] A[1] A[1] + A [ 2 ] A[2] A[2] + A [ 3 ] A[3] A[3] + A [ 4 ] A[4] A[4]
    C C C [ 5 ] [5] [5] = A [ 5 ] A[5] A[5]
    C C C [ 6 ] [6] [6] = A [ 5 ] A[5] A[5] + A [ 6 ] A[6] A[6]
    C C C [ 7 ] [7] [7] = A [ 7 ] A[7] A[7]
    C C C [ 8 ] [8] [8] = A [ 1 ] A[1] A[1] + A [ 2 ] A[2] A[2] + A [ 3 ] A[3] A[3] + A [ 4 ] A[4] A[4] + A [ 5 ] A[5] A[5] + A [ 6 ] A[6] A[6] + A [ 7 ] A[7] A[7] + A [ 8 ] A[8] A[8]

对某个单点的元素进行修改

就先讲加法操作
树状数组支持单点加法操作,根据树状数组的结构特性,节点 x x x 及其所有祖先节点的 C C C 值会受到操作的影响,而任意一个节点的祖先最多有 l o g 2 N log_2 N log2N 个,逐一对 C C C 值进行加法就行了。时间复杂度为 O ( l o g 2 N ) O(log_2 N) O(log2N)
Code:

void update(int x,int y) {  //将第x个数加上y
  for(;x<=N;x+=lowbit(x))  c[x]+=y;
}

而要将某个单点的元素直接改成 y y y,则需要如下代码:

void update(int x,int y) {  //将第x个数改成y
  for(int i=x;i<=N;i+=lowbit(i))  c[i]+=(y-a[i]);
}

查询前缀和

树状数组支持查询前缀和,较为简单,时间复杂度为 O ( l o w 2 N ) O(low_2 N) O(low2N) 直接给出
Code:

long long ask_sum(int x) {  //函数名各式各样
 //查询前x个数的和
  long long ans=0;
  for(;x;x-=lowbit(x))  ans+=c[x];
  return ans;
}

接下来,我们用一道简单的例题来详细讲解一下树状数组

例题

单点修改区间查询
[题目描述]

已知一个数列,你需要进行下面两种操作:

  1. 将某个数加上 x x x
  2. 求出某区间所有数的和。
输入格式

第一行两个整数 n , m n,m n,m, 分别表示该数列长度和操作个数
第二行 n n n 个整数,表示原数列。
接下来 m m m 行每行包含 3 3 3 个整数,表示一个操作,具体如下:
第一个整数为opt,有下列两种输入情况
1 x k: 将第 x x x 个数加上 k k k
2 x y: 询问区间 [ x , y ] [x,y] [x,y] 所有数的和
输出格式
对于每个opt=2,输出一行一个整数,表示相应的结果。

数据范围

1 1 1 < = <= <= n , m n,m n,m < = <= <= 1 0 6 , 10^{6}, 106, 0 0 0 < = <= <= ∣ a [ i ] ∣ , x |a[i]|,x a[i],x < = <= <= 1 0 6 10^{6} 106

树状数组模板题,只是操作2不是求前缀和,是某个区间 [ x , y ] [x,y] [x,y] 的和,我们用 a s k _ s u m ( y ) − a s k _ s u m ( x − 1 ) ask \_ sum(y)-ask \_ sum(x-1) ask_sum(y)ask_sum(x1) 就可以了。
Code:

#include<bits/stdc++.h>
int n,q,opt;
long long a[10000001],c[10000001];
long long x,y;
inline int lowbit(int x) {  //lowbit函数
  return x&(-x);
}
inline void update(int x,long long k) {
  for(;x<=n;x+=lowbit(x))  c[x]+=k;
}
inline long long ask_sum(int x) {
  long long ans=0;
  for(;x;x-=lowbit(x))  ans+=c[x];
  return ans;
}
int main() {
  std::cin>>n>>q;
  for(int i=1;i<=n;i++)  scanf("%lld",a+i),update(i,a[i]);  //输入
  while(q--) {
    scanf("%d %lld %lld",&opt,&x,&y);
    switch(opt) {
    case 1:  //当opt=1时将某个单点元素加上y
      update(x,y); break;
    case 2:  //当等于2时查询某个区间所有数的和
      printf("%lld\n",ask_sum((int)y)-ask_sum((int)x-1)); break;
    }
  }
}

树状数组的模板是非常重要的,虽然说理解也很重要,但在背诵的同时理解的效率会更高。
这篇介绍树状数组的文章到此结束了,如有bug请指出,不胜感激!
下一篇数据结构的文章介绍线段树

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

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

相关文章

Python | Leetcode Python题解之第373题查找和最小的K对数字

题目&#xff1a; 题解&#xff1a; class Solution:def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:m, n len(nums1), len(nums2)# 二分查找第 k 小的数对和left, right nums1[0] nums2[0], nums1[m - 1] nums2[n - 1] 1…

【大模型从入门到精通40】LLM部署运维(LLM Ops)使用Kubeflow Pipelines掌握LLM工作流2

这里写目录标题 使用Kubeflow Pipeline组件&#xff1a;输出与任务对象理解PipelineTask对象通过PipelineTask.output访问输出数据组件函数中的关键字参数实用贴士 在Kubeflow Pipelines中链接组件&#xff1a;传递输出定义依赖组件传递组件输出常见错误&#xff1a;传递正确的…

【Python进阶(十)】——Matplotlib基础可视化

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

Gradle安装使用

下载安装包 Gradle | Releases 解压后在.bash_profile添加环境变量 更新并检查 source ~/.bash_profile

计算机的错误计算(七十二)

摘要 讨论大数的余割函数 csc(x)的错误计算。 例1. 在 Excel 中计算 csc(1234567.89) . 直接贴图&#xff1a; 然而&#xff0c;正确值是 -0.2023325675399672e1&#xff08;ISRealsoft 提供&#xff09;。 Excel 的输出中有 6位错误数字。 例2. 在 Maple中计算 csc(32^58…

回归预测|基于北方苍鹰优化极端梯度提升树的数据回归预测Matlab程序NGO-XGBoost多特征输入单输出

回归预测|基于北方苍鹰优化极端梯度提升树的数据回归预测Matlab程序NGO-XGBoost多特征输入单输出 文章目录 前言回归预测|基于北方苍鹰优化极端梯度提升树的数据回归预测Matlab程序NGO-XGBoost多特征输入单输出 一、NGO-XGBoost模型1. 理解XGBoost2. 理解NGO优化算法3. NGO-XGB…

Kmeans算法原理及Python实现

K-means算法是一种广泛使用的聚类算法&#xff0c;其原理相对简单且易于实现&#xff0c;属于无监督学习的一种。以下是对K-means算法原理的详细解析&#xff1a; 一、基本思想 K-means算法的基本思想是将数据集划分为K个簇&#xff0c;使得每个簇内的数据点尽可能相似&#x…

从0到1使用webpack搭建react脚手架

背景 好多前端童鞋工作多年依然不会使用webpack搭建react脚手架&#xff0c;本文就介绍下如何从零开始搭建一个属于你自己的前端脚手架&#xff0c;提高自己的工程化实力&#xff0c;同时也提高团队的开发效率。 一、基础配置 目标&#xff1a;可以启动最简单的react项目 初…

调用股票网站接口读取大A数据——个股资金流入趋势

以某股票为例&#xff0c;调用自定义的一个类&#xff0c;读取数据。 class BigAData:# 获取资金流向数据def get_money_flow(self, stock_code, page1, num20, sortopendate, asc0):该函数通过股票代码从新浪财经API获取资金流向数据。参数包括股票代码、页数、每页数量、排序…

Elasticsearch安装 Kibana安装

安装Elasticsearch 一、拉取镜像或者上传 docker pull Elasticsearch 二、将上传的镜像导入(在仓库拉取的这一步跳过) docker load -i es.tar docker load -i 三、创建容器 1.Elasticsearch 注意修改到自己的网络&#xff08;第八行&#xff09; docker run -d \--nam…

创意无限,思维升级:2024年思维导图软件新趋势与精选推荐

如何高效地整理思绪、规划项目、乃至进行知识管理&#xff0c;成为了每个人都需要面对的重要课题。xmind思维导图这一强大的思维工具&#xff0c;可以辅助我们对只是进行梳理&#xff0c;这次我就展示一下这类思维导图工具有哪些。 1福晰思维导图 链接一下&#xff1a;https:…

HashMap 的实现原理

说一下 HashMap 的实现原理&#xff1f; JDK1.7 HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元&#xff0c;每一个Entry包含一个key-value键值对。&#xff08;其实所谓Map其实就是保存了两个对象之间的映射关系的一种集合&#xff09;&#xff0c;其中Key 和…

pycharm 出现库已经安装了,但是无法导入的解决方法

打开File - Setttings &#xff0c; 找到 Project interpreter 找到system interpreter 安装

Golang | Leetcode Golang题解之第372题超级次方

题目&#xff1a; 题解&#xff1a; const mod 1337func pow(x, n int) int {res : 1for ; n > 0; n / 2 {if n&1 > 0 {res res * x % mod}x x * x % mod}return res }func superPow(a int, b []int) int {ans : 1for _, e : range b {ans pow(ans, 10) * pow(a…

【Python】家庭用电数据分析Prophet预测

数据集&#xff1a;Household Electricity Consumption | Kaggle 目录 数据集简介 探索性分析 Prophet预测 Prophet模型 Prophet理念 Prophet优点 数据集简介 240000-household-electricity-consumption-records数据集包含了一个家庭6个月的用电数据&#xff0c;收集于2…

vitepress打包异常 build error

今天给vitepress进行打包发布时出现了一个很奇怪的报错。 然后通过git版本回滚发现是正常发布的&#xff0c;说明环境是没有问题的 那么&#xff0c;就看看到改变了哪些文件。 环境版本 vitepress(^1.3.1) node(v18.19.0)猜测1 是文件的内容里面图片找不到导致的错误。猜测2 是…

Ps:首选项 - 历史记录

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“历史记录” History选项卡允许用户更好地管理Photoshop中的编辑历史&#xff0c;确保在需要时能够回溯操作或提供详细的操作记录。 提示&#xff1a; 默认情况下&#xff0c;…

[数据集][目标检测]电力场景输电线防震锤检测数据集VOC+YOLO格式2721张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2721 标注数量(xml文件个数)&#xff1a;2721 标注数量(txt文件个数)&#xff1a;2721 标注…

Go语言操作文件上传和下载应用教程

Go语言操作文件上传和下载应用教程 我们在使用Go的日常开发中&#xff0c;经常会遇到对文件的处理&#xff0c;例如&#xff1a;上传、下载、读写等&#xff08;详情见Go 文件操作基本方法大全&#xff09;&#xff0c;且我们在实际应用中&#xff0c;基本都是使用框架自带的文…

数据结构(Java实现):链表习题

文章目录 1. 题目列表及链接2. 题目解析及代码2.1 删除链表中等于给定值 val 的所有节点2.2 反转一个单链表2.3 给定一个带有头结点 head 的非空单链表&#xff0c;返回链表的中间结点。如果有两个中间结点&#xff0c;则返回第二个中间结点2.4 输入一个链表&#xff0c;输出该…