[数据结构与算法] 树状数组
链接:树状数组_哔哩哔哩 (只有五分钟,多看几遍,就会了)
树结构:(上图是完整树结构,下图是树状数组树结构),代码对这写容易理解。


14.1 作用
- 求前缀和、区间和(跟前缀和的作用一样);
- 元素修改之后能高效更新:(时间复杂度)
- 前缀和:;
- 区间和:;
- 点更新:;
- 点查询:;
- 区间更新:;
14.2 性质
-
每层偶数个数字都是没用的,可用于存储上方层的数值(本层序列和下一序列的和);
-
树状数组中第
pos
个元素所在的序列长度正好为lowbit(pos)
[1] 且以pos
结尾的序列,该序列存储的值是pos
所在的序列的和。因此求pos
的前缀和的过程就是将pos
之前的序列值一直累加。根据这个就可以得到count(int pos)
函数; -
tree[pos]
所在序列的上方的序列,正好就是tree[pos + lowbit(pos)]
,由于lowbit(pos)
是pos
所在序列的长度,因此实际上就是pos
加上所在序列长度就得到上方序列的值的下标。因此树状数组的更新操作就是更新pos
所在的序列以及之上的所有序列。由此可得函数add(int pos, int diff)
。
14.2 代码
#include <bits/stdc++.h>
using namespace std;
// 数组下标从1开始,元素初始化为0
vector<int> nums;
vector<int> tree; // 树状数组,也就是视频中的数组 b
int lowbit(int num) {
return num & (-num);
}
// 更新值:pos为修改的位置,diff为原本元素需加上的新值
void add(int pos, int diff) {
int len = nums.size();
while (pos < len) {
tree[pos] += diff;
pos += lowbit(pos);
}
}
// 求前缀和
long long count(int pos) {
long long res = 0;
while (pos) {
res += tree[pos];
pos -= lowbit(pos);
}
return res;
}
// 求区间和
long long sum(int left, int right) {
return count(right) - count(left - 1);
}
int main() {
int n;
cin >> n;
nums.resize(n + 1); // 数组下标从1开始
tree.resize(n + 1); // 数组下标从1开始
for (int i = 1; i <= n; ++i) { // 注意是i从1到n
cin >> nums[i]; // 修改的时候可能会需要用nums数组计算差值,然后调用add函数更新,本例中未用到该数组
add(i, nums[i]);
}
for (int i = 1; i <= n; ++i) { // 输出树状数组
cout << tree[i] << " ";
}
int left, right;
cout << endl << "输入需要求和的区间:";
cin >> left >> right;
cout << sum(left, right) << endl; // 求区间[left, right]的和
return 0;
}
注解
lowbit(int n)
函数就是求出n的最后一个位 1 所代表的数,例如 12 的二进制位 1100 ,则lowbit(12)
的结果为 4. ↩︎
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
2020-04-03 cmd中的标准文件重定向