数据结构--树状数组

文章目录

    • 1. 树状数组
    • 2. 单点修改
    • 3. 区间修改
    • 4. 完整代码
    • 5. 参考文献

1. 树状数组

类似数据结构:线段树(Segment Tree)

树状数组 跟 线段树 的区别:

  • 树状数组能做的事情,线段树都能做!(线段树功能更牛)
  • 树状数组代码简单,实现起来比线段树容易(树状数组代码更简单)

树状数组的 查询 和 修改 复杂度都为 log⁡(n)\log(n)log(n)

在这里插入图片描述

  • 原数组为 A
  • 树状数组为 C(注意下标从1开始!!!
C1 = A1
C2 = C1 + A2 = A1 + A2
C3 = A3
C4 = C2 + C3 + A4 = A1 + A2 + A3 + A4
C5 = A5
C6 = C5 + A6 = A5 + A6
C7 = A7
C8 = C4 + C6 + C7 + A8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
  • 对 C8 (8的2进制为1000,后面有3个0,所以他管着3个数C4、C6、C7,还有自己A8
  • 奇数末尾没有0,所以奇数位只有自己
  • 那怎么计算一个数管着几个数呢?

2. 单点修改

树状数组的核心函数lowbit(int m):作用是求出 m 的二进制表示的末尾1的位置,对于要查询 m 的前缀和,m = m - lowbit(m) 代表不断对二进制末尾1进行-1操作,不断执行直到 m == 0 结束,就能得到前缀和由哪几个Cm构成

int lowbit(int m){return m & (-m);//(获取最后一个1的10进制值)
}
int getsum(int i){    //求A[1],A[2],...A[i]的和int res = 0;while(i > 0){res += c[i];i -= lowbit(i);}return res;
}

对8(1000)来算下:
补充:负数的二进制是 正数取反末尾+1

8 & (-8) = 1000 & (.111..1000) = 1000 = 8....8-8=0,sum=C8=A[1]+..A[8]
-------------
6 & (-6) =  0110 & (1010) = 10 = 2 .... 6-2=4,sum=C6
4 & (-4) = 0100 & (1100) = 100 = 4.....4-4=0,sum+=C4=C6+C4=A[1]+...A[6]
-------------
奇数上面操作结果均为 1
#include <bits/stdc++.h>
using namespace std;const int N = 8;
int a[N+1]={0,1,2,3,4,5,6,7,8}, c[N+1] = {0}; //对应原数组和树状数组int lowbit(int x){return x&(-x);
}
//修改操作就是getsum的逆过程
void update(int i, int delta){    //在i位置加上deltawhile(i <= N){c[i] += delta;i += lowbit(i);}
}int getsum(int i){    //求A[1],A[2],...A[i]的和int res = 0;while(i > 0){res += c[i];i -= lowbit(i);}return res;
}int main(){for(int i = 1; i <= N; ++i)update(i,a[i]);//读取原数据,插入树状数组cout << getsum(3) << endl;//获取前3个数的和cout << getsum(8) << endl;update(3,2);cout << getsum(3) << endl;cout << getsum(8) << endl;cout << getsum(4)-getsum(2) << endl;//获取A[3],A[4]的区间和return 0;
}

结果:

6
36
8
38
9

3. 区间修改

将原数组的差值A[i]-A[i-1],(A[0]=0)存入树状数组

A[i] = 1 2 3 5 6 9
D[i] = 1 1 1 2 1 3 (差值)

把[2,5]区间内值加上2,则变成了

A[i] = 1 4 5 7 8 9
D[i] = 1 3 1 2 1 1

发现只有LR+1处的值需要修改(左边+,右边-),降低了时间复杂度
所以,update修改的时候,只需要修改两个端点(因为中间的差值不变)
那么上面的 getsum(i) 的结果就是原数组的A[i],很好证明,高中数学(把D[i] 加一下也能发现)


那在上面基础上,我们获取区间的和怎么办?
先来推导一下:
A[i]=D[i]+D[i−1]+...+D[1]A[i−1]=D[i−1]+D[i−2]+...+D[1]...A[2]=D[2]+D[1]A[1]=D[1]A[i] = D[i] + D[i-1]+...+D[1]\\ A[i-1] = D[i-1] + D[i-2]+...+D[1]\\ ... \\ A[2] = D[2] +D[1]\\ A[1] = D[1]A[i]=D[i]+D[i1]+...+D[1]A[i1]=D[i1]+D[i2]+...+D[1]...A[2]=D[2]+D[1]A[1]=D[1]
左右都加起来:
∑x=1iA[x]=i∗D[1]+(i−1)∗D[2]+...+1∗D[i]=i∗∑x=1iD[x]−∑x=1i(D[x]∗(x−1))\sum\limits_{x=1}^i A[x] = i*D[1] + (i-1)*D[2] +... +1*D[i] =i*\sum\limits_{x = 1}^i D[x] - \sum\limits_{x = 1}^i ( D[x]*(x-1))x=1iA[x]=iD[1]+(i1)D[2]+...+1D[i]=ix=1iD[x]x=1i(D[x](x1))
看见最后两项,就明白了吧,再增加一个树状数组 存入 D[x]∗(x−1)D[x]*(x-1)D[x](x1)

#include <bits/stdc++.h>
using namespace std;const int N = 8;
int a[N+1]={0,1,2,3,4,5,6,7,8}, c[N+1] = {0}; //对应原数组和树状数组
int sum1[N+1] = {0}, sum2[N+1] = {0};//对应D[i] , D[i]*(i-1)int lowbit(int x){return x&(-x);
}
//------区间修改-------
void update1(int i, int delta){    //在i位置加上deltaint x = i;//系数不能变,先存起来while(i <= N){sum1[i] += delta;sum2[i] += delta*(x-1);i += lowbit(i);}
}
void update_range(int l, int r, int delta)    //给区间加上delta
{update1(l, delta);//只需修改L,R+1,左+,右-update1(r+1, -delta);
}int query_p(int i){	//A[1],A[2]...A[i]的和int res = 0, x = i;//系数不能变,先存起来while(i > 0){res += x*sum1[i] - sum2[i];i -= lowbit(i);}return res;
}
int query_range(int l, int r)	//区间[l,r]的和
{return query_p(r)-query_p(l-1);
}
int main(){cout << "区间修改,单点查询" << endl;for(int i = 1; i <= N; ++i)update1(i,a[i]-a[i-1]);//读取原数据差值,插入树状数组cout << query_p(3) << endl;//获取前3个数的和cout << query_p(8) << endl;update_range(3,3,2);//修改区间【3,3】,都加上2cout << query_p(3) << endl;cout << query_p(8) << endl;cout << query_range(3,4) << endl;//获取A[3],A[4]的区间和return 0;
}

运行结果

6
36
8
38
9
  • 相关题目:
    程序员面试金典 - 面试题 10.10. 数字流的秩(map/树状数组)
    LeetCode 307. 区域和检索 - 数组可修改(树状数组)

4. 完整代码

/*** @Description: 树状数组* @Author: michael ming* @Date: 2020/4/1 23:38* @Modified by: * @Website: https://michael.blog.csdn.net/*/#include <bits/stdc++.h>
using namespace std;const int N = 8;
int a[N+1]={0,1,2,3,4,5,6,7,8}, c[N+1] = {0}; //对应原数组和树状数组
int sum1[N+1] = {0}, sum2[N+1] = {0}; //对应D[i] , D[i]*(i-1)
int lowbit(int x){return x&(-x);
}
//-------单点修改----------
void update(int i, int delta){    //在i位置加上delta(单点)while(i <= N){c[i] += delta;i += lowbit(i);}
}int query(int i){    //求A[1],A[2],...A[i]的和int res = 0;while(i > 0){res += c[i];i -= lowbit(i);}return res;
}//------区间修改-------
void update1(int i, int delta){    //在i位置加上deltaint x = i;//系数不能变,先存起来while(i <= N){sum1[i] += delta;sum2[i] += delta*(x-1);i += lowbit(i);}
}
void update_range(int l, int r, int delta)    //给区间加上delta
{update1(l, delta);//只需修改L,R+1,左+,右-update1(r+1, -delta);
}int query_p(int i){int res = 0, x = i;//系数不能变,先存起来while(i > 0){res += x*sum1[i] - sum2[i];i -= lowbit(i);}return res;
}
int query_range(int l, int r)//区间[l,r]的和
{return query_p(r)-query_p(l-1);
}
int main(){//单点修改cout << "单点修改,单点查询" << endl;for(int i = 1; i <= N; ++i)update(i,a[i]);//读取原数据,插入树状数组cout << query(3) << endl;//获取前3个数的和cout << query(8) << endl;update(3,2);cout << query(3) << endl;cout << query(8) << endl;cout << query(4)-query(2) << endl;//获取A[3],A[4]的区间和cout << "区间修改,单点查询" << endl;for(int i = 1; i <= N; ++i)update1(i,a[i]-a[i-1]);//读取原数据差值,插入树状数组cout << query_p(3) << endl;//获取前3个数的和cout << query_p(8) << endl;update_range(3,3,2);//修改区间【3,3】,都加上2cout << query_p(3) << endl;cout << query_p(8) << endl;cout << query_range(3,4) << endl;//获取A[3],A[4]的区间和return 0;
}

5. 参考文献

  • 百度百科:树状数组
  • 树状数组入门(简单的原理讲解)
  • 树状数组详解
  • 树状数组 数据结构详解与模板(可能是最详细的了)

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

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

相关文章

python异常数据处理_Python爬虫提高之异常处理

Python爬虫框架之异常处理 任何访问服务器获取数据的请求&#xff0c;都需要做异常处理&#xff0c;当然爬虫更需要我们对各种异常进行处理。只有这样才能提高爬虫的健壮性。如果我们的爬虫足够健壮&#xff0c;那么就能确保程序几个月不停止。 我们从以下几个方面做出讲解&…

BERT部署加速622%,YOLOv7部署加速590%,这款开源压缩神器火了!

导读 众所周知&#xff0c;计算机视觉技术&#xff08;CV&#xff09;是企业人工智能应用比重最高的领域之一。为降低企业成本&#xff0c;工程师们一直在探索各类模型压缩技术&#xff0c;来产出“更准、更小、更快”的AI模型部署落地。而在自然语言处理领域&#xff08;NLP&…

程序员面试金典 - 面试题 10.10. 数字流的秩(map/树状数组)

文章目录1. 题目2. 解题2.1 map2.2 树状数组1. 题目 假设你正在读取一串整数。每隔一段时间&#xff0c;你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。 请实现数据结构和算法来支持这些操作&#xff0c;也就是说&#xff1a; 实现 track(int x) 方法&#xff0c;每读入…

python常用函数的用法_python中常用函数整理

1、map map是python内置的高阶函数&#xff0c;它接收一个函数和一个列表&#xff0c;函数依次作用在列表的每个元素上&#xff0c;返回一个可迭代map对象。 class map(object):""" map(func, *iterables) --> map objectMake an iterator that computes the…

.NET中得到计算机硬件信息

VB.NET中得到计算机硬件信息 本文汇集了在.net中得到计算机硬件信息的一些功能。 得到显示器分辨率 Dim X As Short System.http://dev.21tx.com/os/windows/" target"_blank">Windows.Forms.Screen.PrimaryScreen.Bounds.Width Dim Y As Short System.…

diffusion新高度!可一次性生成200张图??

文 | Pine 明敏&#xff08;凹非寺&#xff09;源 | 量子位给AI一个提示词&#xff0c;一次性出200张图&#xff01;生成速度嗖嗖的&#xff0c;不到3分钟全搞定。喜欢哪张任君挑选&#xff0c;还能直接二次调整编辑。咱就是说&#xff0c;这回用AI画画&#xff0c;终于不废人了…

转正

三个月的时间悄悄的溜走这也是我走出学校之前与社会的一段磨合期感觉自己是幸运的因为在我身边总是可以遇到很多很好的朋友一起陪伴着走过风风雨雨或许生活是残酷的或许我们总是会遇到这样那样不如意的事情但是只要我们勇敢的去面对雨后的天空总会有绚丽的彩虹不要埋怨命运因为…

程序员面试金典 - 面试题 17.09. 第 k 个数(set优先队列/DP)

1. 题目 有些数的素因子只有 3&#xff0c;5&#xff0c;7&#xff0c;请设计一个算法找出第 k 个数。 注意&#xff0c;不是必须有这些素因子&#xff0c;而是必须不包含其他的素因子。 例如&#xff0c;前几个数按顺序应该是 1&#xff0c;3&#xff0c;5&#xff0c;7&…

python怎么封装供java调用_python调用第三方java包实例

先看结果&#xff1a;对于python与java的互调&#xff0c;我一开始是用的py4j,但是后来发现在使用方法的时候&#xff0c;不知道如何在python中导入jar包&#xff0c;然后网上的资料也比较少。后来想不出来办法&#xff0c;又看到有Jpype这个东东。博友们说Jpype的安装比较不好…

推特大裁员后,马斯克与白宫发生冲突!META 大批裁员正在路上

文 | 天于刀刀他来了他来了&#xff01;他带着他的裁员方案走来了&#xff01;带着他的水槽 sink in 的第一天&#xff0c;全球打工人的目光不由自主地聚焦于这个神奇的男人身上&#xff1a;paypal 帮派元老&#xff0c;特斯拉 starlink 创始人&#xff0c;埃隆火星人马斯克&am…

*如何循序渐进向DotNet架构师发展(转)

微软的DotNet开发绝对是属于那种入门容易提高难的技术。而要能够成为DotNet架构师没有三年或更长时间的编码积累基本上是不可能的。特别是在大型软件项目中&#xff0c;架构师是项目核心成员&#xff0c;承上启下&#xff0c;因此RUP方法论也认同以架构为核心&#xff0c;体现4…

如何利用python整合excel_使用 Python 合并多个格式一致的 Excel 文件(推荐)

一 问题描述 最近朋友在工作中遇到这样一个问题&#xff0c;她每天都要处理如下一批 Excel 表格&#xff1a;每个表格的都只有一个 sheet&#xff0c;表格的前两行为表格标题及表头&#xff0c;表格的最后一行是相关人员签字。最终目标是将每个表格的内容合并到一个 Excel 表格…

程序员面试金典 - 面试题 17.07. 婴儿名字(并查集)

1. 题目 每年&#xff0c;政府都会公布一万个最常见的婴儿名字和它们出现的频率&#xff0c;也就是同名婴儿的数量。 有些名字有多种拼法&#xff0c;例如&#xff0c;John 和 Jon 本质上是相同的名字&#xff0c;但被当成了两个名字公布出来。 给定两个列表&#xff0c;一个…

神经网络的简单偏好

文 | 许志钦知乎&#xff08;已授权&#xff09;源 | 天天机器学习作者注记我是2017年11月开始接触深度学习&#xff0c;至今刚好五年。2019年10月入职上海交大&#xff0c;至今三年&#xff0c;刚好第一阶段考核。2022年8月19号&#xff0c;我在第一届中国机器学习与科学应用大…

电影的音乐

Yann Tiersen,法国代表作&#xff1a;《天使爱美丽》《再见&#xff0c;列宁》 对于大家来说&#xff0c;一提到扬蒂尔森(Yann Tiersen)想到的便是《天使爱美丽》。的确《天使爱美丽》使这位法国音乐人一夜成为世界注目的艺术家。在我看来《天使爱美丽》的导演让皮埃尔热内(Jea…

python 列表写入csv_Python将字典数据写入CSV文件

# -*- coding: utf-8 -*- import os import time import csv class WriteCSV(): """定义成员变量""" def __init__(self): pass def init(self, info_list, info_dict_list): self.info_list info_list self.info_dict_list info_dict_list de…

数据结构--并查集(Disjoint-Set)

文章目录1. 并查集2. 操作2.1 初始化2.2 查询2.3 合并2.4 孤立3. 完整代码4. 相关题目5. 参考1. 并查集 并查集是一种树型的数据结构用于处理一些不相交集合&#xff08;Disjoint Sets&#xff09;的合并及查询问题 2. 操作 2.1 初始化 把每个点所在集合初始化为其自身&…

推特裁员大反转!马斯克哭求被裁员工回来

编 | Aeneas 好困源 | 新智元马斯克裁完一半员工后&#xff0c;发现推特运转不了了&#xff0c;现在正哭求一些人回来&#xff0c;堪称爽文情节了。打脸大戏来了&#xff01;马斯克在上周五「灭掉」一半员工后&#xff0c;就后悔了。现在公司正在紧急联系数十名被裁的员工&…

Visual Studio 2008 Shell(翻译)

如果你现在在创建软件开发工具&#xff0c;你现在可以选择基于Visual Studio 2008 Shell。一个流行的Visual Studio 开发环境&#xff0c;Visual Studio Shell提供了一些核心功能&#xff0c;使你能够创建独一无二的应用程序&#xff0c;弹性的自定义能力帮助你推出更加特别的产…

word删除分节符后之前的格式乱了_办公室高级技能之Word邮件合并拆分

当我们需要批量生成名片&#xff0c;合同&#xff0c;成绩单等有相同内容的文件时&#xff0c;使用word邮件合并功能非常方便。如果你还需要把word文件发给不同的人就需要再拆分一下&#xff0c;用复制粘贴的方法拆分word&#xff0c;数量一多不仅效率低&#xff0c;还会因为文…