高级数据结构:树状数组详解(c++实现 求解动态数组区间和问题)

news2024/11/26 23:25:13

文章目录

  • 引入
  • 树状数组
  • c++完整代码

引入

什么是树状数组???

解决数据压缩里的累积频率(Cumulative Frequency)的计算问题,现多用于高效计算数列的前缀和, 区间和,这样的问题可以使用树状数组算法来解决。

  • 前面我们讲过求静态数组的子区间和,我们可以利用前缀和来求得
  • 而现在我们引入动态数组的子区间和这一问题,我们可以使用树状数组。

我们现在通过修改数组,往数组中某一个位置增加或减小一个值,从而导致前缀和数组会发生变化,因此我们不可以使用普通前缀和这一思路来做。
考虑一下:我们求静态数组的前缀和时,如果我们使用暴力法,使用两个for循环来枚举子区间之和,从而得到静态数组的区间和,但是这一复杂度会达到O(N^2),因此使用普通的前缀和可以将复杂度达到O(N)。但是当我们在动态数组中修改元素时,每次修改元素会导致原来的前缀和发生变化,因此我们还需要维护前缀和,这样的时间复杂度也会达到O( N^2)。

但是如果我们使用树状数组这一算法,时间复杂度将会降为O(nlog2n),接下来我将详细解释一下树状数组


树状数组

树状数组包含三个基本的功能:

  • 维护前缀和数组:在某个位置增加减少值时,需要调用此函数,来动态维护前缀和
  • 查询前缀和:求某个区间 [l,r]的区间和,即 sum[r]-sum[l-1]
  • lowbit运算:这个概念非常重要,用于得到某个数的二进制的最低位的一,有什么用呢,我们待会再说。

首先定义tree数组表示一个树状数组tree[i]表示第i个位置的区间的和,区间[l,r]的和即为tree[r]-tree[l-1]

  • 首先来看维护前缀和数组:update的函数意思是在数组的 第i个元素加上元素x,x可以是正负,分别代表tree[i]加或者减一个值
//维护
void update(int i,int x)
{
	/*
	i:下标
	x:增量
	维护:在末尾的1上加1
	*/
	while (i <= n)
	{
		tree[i] += x;
		i += lowbit(i);
	}
}
  • 查询区间的和: 求[l,r]的区间和即为 sum[r]-sum[l-1]
//求和
int sum(int i)
{
	/*
	求和(查询):去除末尾的1
	*/
	int num = 0;
	while (i)
	{
		num += tree[i];
		i -= lowbit(i);
	}
	return num;
}
  • lowbit操作:当前一个数字的补码与原数相与得到的便是当前数字二进制的最后一个1。
inline int lowbit(int x)
{
	return ((x) & (-x));
}


接下来详细解释一个这三个函数基本功能:

引用《百度百科》的一张图来解释
在这里插入图片描述

对于查询的过程:每次去掉二进制的最后的1,例如我们查询sum(7):

  • 7的二进制是 111 ,去掉最后的1,得到:110,即十进制为6,则sum(6)
  • 6的二进制是110,去掉最后的1,得到:100,即十进制为4,则sum(4)
  • 4的二进制是100,去掉最后的1,得到:0,为零则结束

所以说查询sum(7)就等于:sum(7) = sum(6)+sum(4),如上图的蓝色表示。


对于维护的过程:每次在二进制数最后的1上加1,例如我们更新update(1,2):

  • 1的二进制是1,在最后一个1上加1得10,十进制为2,所以说tree[2]也会跟着修改
  • 2得二进制是10,在最后一个1上加1得100,十进制为4,所以说tree[4]也会跟着修改
  • 4得二进制是100,在最后一个1上加1得1000,十进制为8,所以说tree[8也会跟着修改

所以说当维护(修改一个位置的元素值)时,我们会自动动态的维护整个树状数组,修改tree[1]+=2 ,tree[2] tree[4] tree[8] … 都会跟着修改。,一直到整个数组的大小N为止。


对于lowbit运算的过程:

lowbit运算可以得到二进制最后一个1的位置,同时也可以根据lowbit确定tree数组的构成

首先我们需要理解tree数组的意思:tree数组本质是前缀和,即tree[i]=a[1]+a[2]+…+a[i]的区间的和。

接下来我们枚举1-7数字的二进制以及lowbit运算的结果,看看lowbit运算是如何影响tree数组的

在这里插入图片描述
首先我们可以了解到:

  • 数字i为奇数时,lowbit始终结果为1,因此我们就可以推断这个1代表tree[i]=a[i] ,代表它只有一个原数组a[i]的值
  • 数字i为偶数时,lowbit结果似乎从数字2,4来看满足:2的时候有两项,4的时候有四项吗,但是并不是这样!!!!!!比如数字6就只有2项。当i为偶数的时候,实际上看的是二进制的最后一位数字1及其之后所表示的低位的值,例如 10就是2;100就是4,110就是2(10);11110还是2(最后一位1与后面的0);11000就是8

因此:tree数组的每一项的值:

  1. 当数字为1时,lowbit运算为1,所以tree[1]=a[1] (奇数肯定只有一项)
  2. 当数字为2时,lowbit运算为10,所以tree[2]=a[2]+a[1],有两项
  3. 当数字为3时,lowbit运算为1,所以tree[3]=a[3]
  4. 当数字为4时,lowbit运算为100,所以tree[4]=a[4]+a[3]+a[2]+a[1] ,有四项
  5. 当数字为5时,lowbit运算为1,所以tree[5]=a[5]
  6. 当数字为6时,lowbit运算为10,所以tree[6]=a[6]+a[5] ,有两项
  7. 当数字为7时,lowbit运算为1,所以tree[7]=a[7]
  8. 当数字为8时,lowbit运算为1000,所以tree[8]=a[8]+ … a[1],有八项

即我们可以得到这样一张图:

请仔细理解这张图代表的意义!!!!!!!!!
在这里插入图片描述
对于tree[4]=a[1]+a[2]+a[3]+a[4],其实就是tree[4]=tree[3]+a[4],对于其他也是同理。


c++完整代码

#include <bits/stdc++.h>
using namespace std;

/*
树状数组:常用于对动态区间的查询操作,即求动态数组求前缀和的操作
三大基本功能:
1. 维护 update
2. 求和 sum
3. lowbit运算
*/
const int N = 10005;
int tree[N]{ 0 };
int n;		//当前tree数组的最大元素个数
inline int lowbit(int x)
{
	return ((x) & (-x));
}

//维护
void update(int i,int x)
{
	/*
	i:下标
	x:增量
	维护:在末尾的1上加1
	*/
	while (i <= n)
	{
		tree[i] += x;
		i += lowbit(i);
	}
}

//求和
int sum(int i)
{
	/*
	求和(查询):去除末尾的1
	*/
	int num = 0;
	while (i)
	{
		num += tree[i];
		i -= lowbit(i);
	}
	return num;
}
int main()
{
	n = 5;
	for (int i = 1; i <= 5; i++)
	{
		update(i, 3);
	}
	// 3 6 9 12 15
	cout << "S[2,4]: " << sum(4) - sum(1) << endl;

	//更新数组元素
	// 3 6 15 18 21
	update(3, 6);

	cout << "S[2,4]: " << sum(4) - sum(1) << endl;
	return 0;
}

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

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

相关文章

蓝桥杯重点(C/C++)(随时更新,更新时间:2023.1.27)

点关注不迷路&#xff0c;欢迎推荐给更多人 目录 1 技巧 1.1 取消同步&#xff08;节约时间&#xff0c;甚至能多骗点分&#xff0c;最好每个程序都写上&#xff09; 1.2 万能库&#xff08;可能会耽误编译时间&#xff0c;但是省脑子&#xff09; 1.3 蓝桥杯return 0…

深度学习:CSPNet

深度学习&#xff1a;CSPNet前言解决的问题Method目前主流网络存在的问题Partial Dense BlockPartial Transition LayerExact Fusion Model实验前言 CSPNet 是作者 Chien-Yao Wang 于 2019 发表的论文 CSPNET: A NEW BACKBONE THAT CAN ENHANCE LEARNING CAPABILITY OF CNN。也…

【C语言】数据结构-单链表

主页&#xff1a;114514的代码大冒险 qq:2188956112&#xff08;欢迎小伙伴呀hi✿(。◕ᴗ◕。)✿ &#xff09; Gitee&#xff1a;庄嘉豪 (zhuang-jiahaoxxx) - Gitee.com 文章目录 目录 文章目录 前言&#xff08;链表的优势&#xff09; 一、单链表是什么 二、单链表操作…

Hadoop基础之《(2)—Hadoop概述》

一、Hadoop是什么 1、Hadoop是一个由Apache基金会所开发的分布式系统基础架构。 2、主要解决&#xff0c;海量数据的存储和海量数据的分析计算问题。 3、广义上来说&#xff0c;Hadoop通常是指一个更广泛的概念—Hadoop生态圈。 二、Hadoop的三大发行版本 Apache版本&#x…

【论文简述】Long-range Attention Network for Multi-View Stereo(WACV 2021)

一、论文简述 1. 第一作者&#xff1a;Xudong Zhang 2. 发表年份&#xff1a;2021 3. 发表期刊&#xff1a;WACV 4. 关键词&#xff1a;MVS、注意力、级联、监督回归 5. 探索动机&#xff1a;先前的方法忽略了像素之间的依赖关系&#xff0c;并且期望回归的方式效率不高。…

Kettle(10):switch/case组件

1 机智的体育老师——条件判断 有一天,体育老师要让班上的男女同学分别排成两队。但这个班上还有几名同学,很特殊——他们是蜘蛛!!所以,机智的体育老师需要把他们排成三队,男同学一队,女同学一队,蜘蛛一队。 体育老师要做一件非常重要的事情:判断学生是男孩还是女孩、…

联想E14 开机黑屏报错2102:Detection error on HDD0 (Main HDD)

环境&#xff1a; 联想E14 Win10专业版 问题描述&#xff1a; 联想E14 开机黑屏报错2102&#xff1a;Detection error on HDD0 (Main HDD) 解决方案&#xff1a; 1.关机重启 2.尝试拆解&#xff0c;拔出硬盘数据线&#xff0c;重新安装

Scanpy 单细胞基因分析

参考&#xff1a;https://www.bilibili.com/video/BV1sq4y1C7Qx/ https://scanpy-tutorials.readthedocs.io/en/latest/pbmc3k.html 代码下载&#xff1a;scanpy分析scRNA-seq数据基本流程&#xff08;含scanpy seurat两大工具对比&#xff09; 链接: https://pan.baidu.com/s…

华为机试 HJ33 整数与IP地址间的转换

华为机试题 HJ33 整数与IP地址间的转换 一、题目描述 描述原理&#xff1a;ip地址的每段可以看成是一个0-255的整数&#xff0c;把每段拆分成一个二进制形式组合起来&#xff0c;然后把这个二进制数转变成一个长整数。举例&#xff1a;一个ip地址为10.0.3.193每段数字 …

C# - JSON Schema validation

C# - JSON Schema validation引言如何生成 C# 类 JSON Schema利用在线工具利用 Visual Studio利用 NJsonSchema验证 JSON Schema针对 JSON Schema 字符串针对 C# 类 Schema引言 针对 API 测试&#xff0c;我们需要验证 Response 中的一些字段类型&#xff0c;是否缺省等&#…

Linux操作系统精讲之高级IO

代码在&#xff1a; https://github.com/sjmshsh/System-Call-Learn 通过阅读本篇文章&#xff0c;你可以收获&#xff1a; 理解五种IO模型的基本概念&#xff0c;重点是IO多路转接掌握select&#xff0c;poll&#xff0c;epoll系统调用接口并实现简易的TCP服务器理解epoll的…

10、创建不同类型的工程

文章目录10、创建不同类型的模块10.1 创建Java模块10.2 创建Java Web模块1 IDEA中配置Tomcat2 创建Web工程3 配置Web工程并运行4 乱码的解决10.3 创建Maven Java模块1 Maven的介绍2 Maven的配置3 Maven Java工程的创建4 编写代码及测试10.4 创建Maven Web模块1 创建Maven的Web模…

java基础语法——断点调试与数据加密(基础语法练习学习)

目录 Eclipse的断点调试 基础语法的练习 Eclipse的断点调试 作用&#xff1a;查看程序执行流程和调试程序 断点&#xff1a; 就是一个标记,就是我们经常用到的debug&#xff08;检查程序错误&#xff0c;我们用到的是debug as&#xff09; A–哪里加?—— 在实际的程序行号…

Knowledge-based-BERT(二)

多种预训练任务解决NLP处理SMILES的多种弊端&#xff0c;代码&#xff1a;Knowledge-based-BERT&#xff0c;原文&#xff1a;Knowledge-based BERT: a method to extract molecular features like computational chemists&#xff0c;代码解析继续K_BERT_WCL_pretrain。模型框…

Java多线程 - 线程安全和线程同步解决线程安全问题

文章目录线程安全问题线程同步方式一: 同步代码块方式二: 同步方法方式三: Lock锁线程安全问题 线程安全问题指的是: 多个线程同时操作同一个共享资源的时候可能会出现业务安全问题&#xff0c;称为线程安全问题。 举例: 取钱模型演示 需求&#xff1a;小明和小红是一对夫妻&am…

建单向链表-C语言实现

任务描述 本关需要你建立一个带头结点的单向链表。 相关知识 什么是链表?链表和二叉树是C语言数据结构的基础和核心。 链表有多种形式,它可以是单链接的或者双链接的,可以是已排序的或未排序的,可以是循环的或非循环的。 本关让我们来学习单链表。 单链表 单向链表(单…

XC-16 SpringSecurity Oauth2 JWT

SpringSecurityOauth2用户认证需求分析用户认证与授权单点登录需求第三方认证需求用户认证技术方案单点登录技术方案Oauth2认证Oauth2认证流程2.2.2Oauth2在本项目中的应用SpringSecurity Oauth2认证解决方案SpringSecurityOauth2研目标搭建认证服务器导入基础工程创建数据库Oa…

一起自学SLAM算法:9.2 LSD-SLAM算法

连载文章&#xff0c;长期更新&#xff0c;欢迎关注&#xff1a; 下面将从原理分析、源码解读和安装与运行这3个方面展开讲解LSD-SLAM算法。 9.2.1 LSD-SLAM原理分析 前面已经说过&#xff0c;LSD-SLAM算法是直接法的典型代表。因此在下面的分析中&#xff0c;首先介绍一下直…

学习笔记:Java 并发编程④

若文章内容或图片失效&#xff0c;请留言反馈。 部分素材来自网络&#xff0c;若不小心影响到您的利益&#xff0c;请联系博主删除。 视频链接&#xff1a;https://www.bilibili.com/video/av81461839配套资料&#xff1a;https://pan.baidu.com/s/1lSDty6-hzCWTXFYuqThRPw&am…

CSS语法格式与三种引入方式

文章目录第一章——CSS简介1.1 CSS语法格式1.2 CSS 位置1.3 CSS引入方式1.3.1.行内样式表&#xff08;内联样式表&#xff09;1.3.2 外部样式表1.3.3 内部样式表第一章——CSS简介 1.1 CSS语法格式 CSS 规则由两个主要的部分构成&#xff1a;选择器以及一条或多条声明。 选择…