导言

深藏于算法与数据结构中的思想非常的美妙,尤其是当我们一个一个攻克其中的难点,体会其中蕴含的"哲理"时, A 题的自信力也会有所增加,心情也会格外的舒爽。最近重新接触了树状数组和线段树的题目,决定对其进行一定程度上的系统梳理,并与大家分享,如有不足之处,还请指教,大家共同进步

树状数组

1.基本概念

​ 顾名思义,就是像树一样的数据结构,与 Trie 树类似,其结构类型为完全二叉树,节点排列非常的有规律,故我们直接可以使用数组来模拟,以达到简洁而高效的目的

假设我们有一长为 n 的序列 {a1, a2 , ····,an}, 进行以下操作

(1) 单 点 修 改 \color{Orange}单点修改 单点修改: add(x, v)

​ 表示在 第 x 位置上 加上一个数值 v

(2) 区 间 查 询 \color{Orange}区间查询 区间查询: query(x)x <= n

​ 表示区间 [1, x] 上的和,即 query(x) = a1 + a2 + ··· + ax。那么我们可以得出区间[x, y] 上的区间和即为 query(y) - query(x - 1) (前缀和思想)

对于上述操作在 n很小的情况下,我们完全可以使用差分与前缀和来操作,复杂度是 O(n)。但是,如果 n很大的情况下,这样的做法效率就会非常低。此时我们就需要一种高效的数据结构来用空间换取时间,也就是树状数组

2.原理推导及如何建立树状数组

如下图所示,我们有

  • 数组A: 传入数据的原数组
  • 数组C:建立起来的树状数组

( 1 ) 区 间 查 询 − − O ( l o g n ) \color{Turquoise}(1) 区间查询 - -O(logn) (1)区间查询−−O(logn)

从图中我们可以得出,对于树状数组 C 来说

C [ 1 ] \color{green}C[ 1 ] C[1] = A[ 1 ]

C [ 2 ] \color{green}C[ 2 ] C[2] = A[ 2 ] + C [ 1 ] \color{green}C[ 1 ] C[1]

C [ 3 ] \color{green}C[ 3 ] C[3] = A[ 3 ]

C [ 4 ] \color{green}C[ 4 ] C[4] = A[ 4 ] + C [ 3 ] \color{green} C[ 3 ] C[3] + C [ 2 ] \color{green}C[ 2 ] C[2]

C [ 5 ] \color{green}C[ 5 ] C[5] = A[ 5 ]

C [ 6 ] \color{green}C[ 6 ] C[6] = A[ 6 ] + C [ 5 ] \color{green}C[ 5 ] C[5]

C [ 7 ] \color{green}C[ 7 ] C[7] = A[ 7 ]

C [ 8 ] \color{green}C[ 8 ] C[8] = A[ 8 ] + C [ 7 ] \color{green}C[ 7 ] C[7] + C [ 6 ] \color{green}C[ 6 ] C[6]+ C [ 4 ] \color{green}C[ 4 ] C[4]

​ ~~~~~~        = A[ 1 ] + A[ 2 ] + A[ 3 ] + A[ 4 ] + A[ 5 ] + A[ 6 ] + A[ 7 ] + A[ 8 ]

初步发现,对于每一个 C[ x ],我们有

  1. 若 x 为 奇 数 \color{Orange}奇数 奇数,C[ x ] = A[ x ]

  2. 若 x 为 偶 数 \color{Orange}偶数 偶数,则不然:

    C[ 2 ] = A[ 2 ] + A[ 1 ]

    C[ 4 ] = A[ 4 ] + A[ 3 ] + A[ 2 ] + A[ 1 ]

    C[ 6 ] = A[ 6 ] + A[ 5 ]

    C[ 8 ] = A[ 8 ] + A[ 7 ] + A[ 6 ] + A[ 5 ] + A[ 4 ] + A[ 3 ] + A[ 2 ] + A[ 1 ]

根据数学归纳可以得出,此时 C [ x ] = A [ x − 2 k + 1 ] + A [ x − 2 k + 2 ] + A [ x − 2 k + 3 ] + ⋅ ⋅ ⋅ + A [ x ] C[ x ] = A[x - 2^k + 1] + A[x - 2^k + 2] + A[x - 2^k + 3] +···+ A[x] C[x]=A[x−2k+1]+A[x−2k+2]+A[x−2k+3]+⋅⋅⋅+A[x]

带入发现对于 X 为奇数时,也满足这个规律,所以在树状数组中,

( 核 心 ) \color{Red}(核心) (核心) C[ x ] 表示 区间 【 x − 2 k + 1 , x 】 \color{sKyblue}【x - 2^k + 1, ~x】 【x−2k+1, x】 的和

k 表示 x 的二进制表示中从最低位到最高位连续零的长度,也就是最后一位 1 的位置,我们可以通过一个名为 l o w b i t \color{Maroon}lowbit lowbit 的操作将其求出

int lowbit (int x)
{return x & -x ;//返回 x 的最后一位 1
}

如 x = 1010,则 lowbit(x)= 10

​ x = 101000,则lowbit(x)= 1000

所以, C[ x ] = 【 x − 2 k + 1 , x 】 【x - 2^k + 1, ~x】 【x−2k+1, x】

​ = 【 x − l o w b i t ( x ) + 1 , x 】 \color{sKyblue}【x - lowbit(x) + 1, ~x】 【x−lowbit(x)+1, x】的和

有了以上结论,对于树状数组来说,我们如何求出下图区间【1, x】 的区间和呢?

既然 C[ x ] 表示区间【x - lowbit(x) + 1, x】的和,那么【1, x】区间的和就是 C[ x ] + 递归段,即 C[ x ] + C[ x - lowbit(x) ] + ··· + C[ 1 ]

int query(int x)// 求出区间 【1 ,x】的和
{int ans = 0;for(int i = x; i ; i -= lowbit(i))ans += c[i];return ans;
}

( 2 ) 单 点 修 改 − − O ( l o g n ) \color{Turquoise}(2) 单点修改- - O(logn) (2)单点修改−−O(logn)

​ 对于单点修改,也就是 在某个位置 x 加上一个数值 V,如下图所示,假设我们对数组C[ 3 ]位置进行更新时,那么它相应的父亲节点存储的和也需要进行更新,可以发现更新就是一个 “爬树” 的过程。一直把修改信息往上传递,直到到达树的最大高度,也就是树状数组的最大容量 MAXN

对于区间查询,我们可以形象化为一个 “下树” 的过程,那么单点修改可以看作它的 “逆运算”,即往高的区间走,所以我们就可以得到下列单点修改的代码段了:

const int MAXN = 100010;
void add(int x, int v)
{for(int i = x; i < MAXN; i += lowbit(i) )c[i] += v;
}

关于树状数组的正确性证明,大家感兴趣的话可以参考一下这篇博客 树状数组正确性证明

3.代码模板

目前为止,我们尚未提及如何来用原数组 A 来初始化树状数组 C,其实对于树状数组的初始化,我们主要有两种方式

  1. 暴 力 构 造 树 状 数 组 C [ x ] \color{Red}暴力\color{White}构造树状数组 C[x] 暴力构造树状数组C[x] : 就是直接对每个位置 x 进行一次单点修改,一共进行 n 次操作,时间复杂度为 O ( n l o g n ) \color{Purple}O(nlogn) O(nlogn)

    void init()
    {for(int i = 1; i <= n; i ++ )add(i, a[i]);
    }
    
  2. 线 性 构 造 树 状 数 组 C [ x ] \color{Green}线性\color{White}构造树状数组 C[x] 线性构造树状数组C[x]:考虑到树状数组的本质,我们知到一个C[x] 管辖的区域是【x - lowbit(x) + 1 ,x】中所有数的和,所以我们可以先对数组A求一个前缀和,利用前缀和 S[ x ] - S[x - lowbit(x)] 来更新数组C[x],即 C[ x ] = S[x] - S[x - lowbit(x)],这样就能线性构造了,时间复杂度为 O ( n ) \color{Purple}O(n) O(n)

    void init()
    {for(int i = 1; i <= n; i ++ )// 求a的前缀和s[i] = s[i - 1] + a[i];for(int i = 1; i <= n; i ++ )// 用前缀和求出cc[i] = s[i] - s[i - lowbit(i)];
    }
    

故此我们即可得到完整的树状数组的模板

const int MAXN = 100010;
// 注意树状数组最大空间一般开到 1e5 + 10int a[MAXN];// 原数组
int c[MAXN];// 树状数组
int n; // 题目所给的数组 A 数据长度int lowbit(int x)// 返回最后一位 1,即 2^k
{return x & -x;
}
int query(int x)// 查询区间 [1, x] 的和
{int ans = 0;for(int i = x; i ; i -= lowbit(i))ans += c[i];return ans;
}
void add(int x, int v)//修改位置 x 的值
{for(int i = x; i < MAXN; i += lowbit(i))c[i] += v;
}
void init()// 初始化, 这里我选用O(nlogn)的方式
{for(int i = 1; i <= n; i ++ )add(i, a[i]);
}

以上内容尚未完全,随着今后学习的推进,我会继续对其进行补充与完善。另外,大家如果觉得我写的还行的话,还请赠予我一个可爱的赞,你的赞对于我是莫大的支持。

【预告】

  1. 树状数组的拓展及例题
  2. 线段树详解

树状数组详解(附图解,模板及经典例题分析)相关推荐

  1. 【数据结构】树状数组详解(Leetcode.315)

    前言 最近做题时遇到一个关于树状数组的题力扣https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/但是CSDN上仅有 ...

  2. szu 寒训第二天 树状数组 二维树状数组详解,以及树状数组扩展应用【求逆序对,以及动态第k小数】

    树状数组(Binary Index Tree) 树状数组可以解决可以转化为前缀和问题的问题 这是一类用以解决动态前缀和的问题 (有点像线段树简版) 1.对于 a1 + a2 + a3 + - + an ...

  3. AcWing 241 楼兰图腾(树状数组详解)

    树状数组 问题引入 树状数组是一种实现起来比较简单的高级数据结构. 我们知道,对于一个数组a[i],其前缀和s[i]表示a数组里面前i个元素之和,而求区间l到r的元素之和可以用s[r] - s[l-1 ...

  4. 树状数组详解(超详细)(完整代码在四 五最后)

    一,树状数组的优点 前缀和的思想,可以通过O(n)的预处理,使得多次查询区间值都是o(1),但只能解决不修改,多次查询的问题. 差分思想,能通过差分数组,将区间修改变成O(1)的,最后通过一次O(n) ...

  5. Win+TexLive2020+TexStudio安装过程详解附ElsevierLatex模板下载并使用

    Win+TexLive2020+TexStudio安装过程详解附ElsevierLatex模板下载并使用 一.下载并安装Texlive2020 1.下载TexLive2020 2.安装过程 解压之后运 ...

  6. Tre树(字典树)数据结构详解(图解)及模板

    了解这个数据结构之前我们需要了解它能被用来做什么 字典树又称单词查找树,Tire树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引 ...

  7. 递归算法详解——递归算法的三要素以及例题分析

    目录 1递归的三要素 1.1明确函数的功能 1.2递归的结束条件 1.3函数的等价关系 2递归案例 递归算法(英语:recursion algorithm)是指一种通过重复将问题分解为同类的子问题而解 ...

  8. 数据结构--树状数组

    文章目录 1. 树状数组 2. 单点修改 3. 区间修改 4. 完整代码 5. 参考文献 1. 树状数组 类似数据结构:线段树(Segment Tree) 树状数组 跟 线段树 的区别: 树状数组能做 ...

  9. 线。段。树--树状数组-主席树

    简单了解一下线段树 以前写过的内容,搬运过来 线段树的应用场景:满足区间加法性质且多次查询,什么是区间加法性质,比如最大值,求和,树状数组.线段树.主席树依次. 线段树框架:建树--查询--更新... ...

最新文章

  1. AngularJS 表单数据验证及错误信息提示
  2. 用php实现Google /Baidu Ping服务快速收录
  3. 移动端页面按手机屏幕分辨率自动缩放的js
  4. java如何实现e的次方_java 大数据次方运算
  5. 阿里、美团、滴滴产品经理共述:产品经理的“乐”与“伤”
  6. 自定义日历控android,android 一个简单的自定义日历控件,让你掌控时间
  7. 从Slice_Header学习H.264(三.3)--相关细节之 FMO
  8. linux HUSTOJ 一些页面修改
  9. 使用人工智能加快海底数据处理-从粗略过滤到精细智能数据筛选
  10. 哈工大计算机系统2022大作业:程序人生-Hello‘s P2P
  11. Gimp 将图片中的颜色更改
  12. Gamit10.6基线解算和网平差
  13. 新手购买服务器搭建属于自己的网站(详细版)
  14. vscode一键生成佛祖保佑永无bug
  15. 爬取软科中国最好大学排名
  16. c语言开机自启动 linux_Linux开机启动程序rc.local
  17. Quartus II 操作入门
  18. Python(x,y)安装
  19. 光电二极管的采样电路
  20. 哈工大计算机网络研究生,2020年哈尔滨工业大学软件工程考研经验分享

热门文章

  1. 【转载】让盐和味精告诉你,「变量」是什么 | 亲子课堂 第 4 课
  2. 人机的根本区别:Free will
  3. GPS设置及原理教程-如何通过修改gps.conf文件来提高搜星速度
  4. 海康威视:笔试题(20190908)
  5. 全国首个数字产权区块链平台上线,共享购模式悄然上市
  6. 浅谈领域驱动设计(DDD:Domain-Driven Design)
  7. 视频号领域发布的不同时间,视频号上热门秘诀:国仁楠哥
  8. Android 隐藏/透明小白条
  9. 工具类批量修改照片的名字
  10. 腾讯云轻量服务器Centos 7.6安装redis 5.0.4教程