当前位置: 首页 > news >正文

【数据结构】树状数组

树状数组

树状数组,正如起名是一个逻辑上为树结构的数组,其结构如下图所示

在这里插入图片描述

可见其二进制位从低位到高位,0的个数决定该结点在第几层,例如结点1(00001),从低位到高位有0个0,所以在第0层,结点4(00100),从低位到高位有2个0,所以在第2层

通过观察,还能看到,一个结点的父结点是该结点加上lowbit(x)

树状数组t[i] 存储的是(x - lowbit(x), x]的值(左开右闭)

其中lowbit(x) 表示的是能够返回一个二进制序列从最后一位1的结果

如果x为10100,取反后结果是01011,再加一则变为了01100,再与10100做与运算后变为00100

在这里插入图片描述

了解了树状数组的结构,那么树状数组有什么应用

  • 单点修改
  • 区间查询

这两个是树状数组最基本的应用

单点修改的操作是在单点修改的同时,迭代更新父节点

/**
* 在x个数加上k
*/
int add(int x,int k)
{
	for(int i=x;i<=n;i+=lowbit(i))
	t[i]+=k;
}

区间查询就是迭代加上t[i],每次i -= lowbit(i)

/**
* 查询从第1-x个数的和
*/
int query(int x) {
    int res = 0;
    for(int i = x; i > 0; i -= lowbit(i)) {
        res += tree[i];
    }
    return res;
} 
【例题】

给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和。

输入格式

第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。

第二行包含 n 个整数,表示完整数列。

接下来 m 行,每行包含三个整数 k,a,b (k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。

数列从 1 开始计数。

输出格式

输出若干行数字,表示 k=0 时,对应的子数列 [a,b] 的连续和。

数据范围

1≤n≤100000,
1≤m≤100000,
1≤a≤b≤n,
数据保证在任何时候,数列中所有元素之和均在 int 范围内。

输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
输出样例:
11
30
35
import java.io.*;
import java.util.*;
public class Main {
    static final int N = 100010;
    //原数组
    static int[] a = new int[N];
    //树状数组
    static int[] tree = new int[N];
    //实际树状数组长度
    static int n;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] row1 = br.readLine().split(" ");
        n = Integer.parseInt(row1[0]);
        int m = Integer.parseInt(row1[1]);
        //读取原数组,从下标1开始 
        String data[] = br.readLine().split(" ");
        for(int i = 1; i <= n; i++) {
            a[i] = Integer.parseInt(data[i - 1]);
        }
        for(int i = 1; i <= n; i++) {
            add(i, a[i]);
        }
        while(m-- > 0) {
            String[] op = br.readLine().split(" ");
            int o = Integer.parseInt(op[0]);
            int x = Integer.parseInt(op[1]);
            int y = Integer.parseInt(op[2]);
            if(o == 1) {
                add(x, y);
            } else if(o == 0) {
                int res = query(y) - query(x - 1);
                System.out.println(res);
            }
        }
        br.close();
    }
    static int lowbit(int x) {
        return x & (~x + 1);
    }
    static void add(int x, int v) {
        for(int i = x; i <= n; i += lowbit(i)) {
            tree[i] += v;
        }
    }
    static int query(int a) {
        int res = 0;
        for(int i = a; i > 0; i -= lowbit(i)) {
            res += tree[i];
        }
        return res;
    } 
}
操作时间复杂度
addO(logn)
queryO(logn)

与前缀和算法的比较

前缀和算法无法实现动态求前缀和,如果只需要求一次前缀和之后不再修改,前缀和算法自然比树状数组要好

因为前缀和算法时间复杂度如下

操作时间复杂度
initO(n)
queryO(1)
http://www.dtcms.com/a/122999.html

相关文章:

  • RTT中断管理学习
  • 苹果电脑MAC系统安装
  • 【MySQL篇】mysqlpump和mysqldump参数区别总汇
  • 【C++游戏引擎开发】第11篇:GLFW、GLAD环境搭建与第一个三角形渲染
  • 09-Spring 与线程安全:IOC 与多线程下的坑与解法
  • 解锁Midjourney创作潜能:超详细提示词(Prompts)分类指南
  • 【42期获取股票数据API接口】如何用Python、Java等五种主流语言实例演示获取股票行情api接口之沪深指数最新分时BOLL数据及接口API说明文档
  • 三、使用Keil5新建STM32工程
  • 【学Rust写CAD】29 Alpha256结构体(alpha256.rs)
  • torch.meshgrid()
  • 【OCR】总结目前流行的主要的OCR工具
  • Jenkins安装流程
  • 联邦学习研读笔记
  • printf
  • 【NLP 面经 9、逐层分解Transformer】
  • 第十一章 Python语言-高阶技巧(终章)
  • Dubbo(44)如何排查Dubbo的服务依赖问题?
  • 17. git pull
  • 6、nRF52xx蓝牙学习(nrf_gpiote.c库函数学习)
  • 基于 AI智能体、大模型、RAG、Agent 等技术构建公司内部闭环智能问答系统的详细方案,结合 Spring Boot + Vue 管理系统 的改造思路
  • Http代理服务器选型与搭建
  • Starrocks的Bitmap索引和Bloom filter索引以及全局字典
  • 基于微信小程序的志愿服务系统的设计与实现
  • 数字图像处理作业3
  • fuse-python使用fuse来挂载fs
  • 汽车软件开发常用的建模工具汇总
  • Joomla 常用模块 - 在线用户与Joomla 常用模块 - 自定义HTML模块
  • [leetcode]判断质数
  • 关于C++日志库spdlog
  • JS 函数提升