算法基础-树状数组

今天我们分享一下树状数组,前置知识-了解树的结构,知道什么是左右儿子,各个节点的名称,也就是有点基础吧。今天以一个实际问题引出树状数组吧,中查询l-r的区间。(以B站大佬的课件为例子,可以关注下,在最后放上链接)

如果是暴力的话,显然时间复杂度是接受不了的(o(n方)),为了解决这个问题,我们就要用一些高级的数据结构。就是我们今天介绍的树状数组。

首先看下树状数组是什么,

树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。

这种数据结构(算法)并没有C++和Java的库支持,需要自己手动实现。在Competitive Programming的竞赛中被广泛的使用。树状数组和线段树很像,但能用树状数组解决的问题,基本上都能用线段树解决,而线段树能解决的树状数组不一定能解决。相比较而言,树状数组效率要高很多。(百度上的)

然后在介绍前缀和,b[1] = a[1],b[2] = a[1] + a[2],b[3] = a[1] + a[2] +a[3]........b[n] = a[1] + a[2] + a[3] + ..... + a[n].这就是前缀和的概念,其实树状数组就是在维护一个前缀和。

再介绍lowbit函数,用于再左右儿子或者是父节点,伪代码为 return x & (-x),就是返回某个数二进制从右往左的第一个一所代表的数,x于lowbit函数的关系如下、

一个左儿子的父节点表示为x + lowbit(x),同理右儿子的父亲表示为 x - lowbit(x),把图画出来,是不是很像二叉搜索树,

最后,给个树状数组的模板题吧,

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入输出格式

输入格式:

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

输出格式:

输出包含若干行整数,即为所有操作2的结果。(洛谷的题,https://www.luogu.org/problemnew/show/P3372)

输入样例

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

输出样例

11
8
20

附上AC代码

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 1e7 + 5;
int n,m,a;
int d[N];     //存前缀和的数组

int lowbit(int x)
{
    return x & (-x);
}

int query(int x)  //查询操作
{
    int res = 0;
    while(x)    //x > 0,x从左边找父亲节点相加
    {
        res += d[x];
        x -= lowbit(x);
    }
    return res;
}

void add(int x,int val)
{
    while(x <= n)
    {
        d[x] += val;
        x += lowbit(x);   //从左到右构建树状数组
    }
}

int sum(int x)
{
    int total = 0;
    while(x != 0)
    {
        total += d[x];
        x -= lowbit(x);
    }
    return total;
}

int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
    {
        cin >> a;
        add(i,a);
    }
    while(m--)
    {
        int f,x,y;
        cin >> f >> x >> y;
        if(f == 1)
        {
            add(x,y);
        }
        if(f == 2)
        {
            cout << sum(y) - sum(x - 1) << endl;
        }

    }
    return 0;

}

最后的最后附上B站大佬的链接,https://www.bilibili.com/video/av36663299?from=search&seid=7911780148837730858

原文地址:https://www.cnblogs.com/gauss191/p/10546255.html

时间: 2024-11-05 22:35:31

算法基础-树状数组的相关文章

一天一道算法题——树状数组

题目[模板]树状数组1:https://www.luogu.com.cn/problem/P3374 树状数组和线段树差不多,可以处理区间操作,但是处理不了太复杂的区间问题.,不过代码比线段树简洁很多很多!!!时间复杂度都为O(logn). 例如,区间[1,8]存储方式如下: 1 tree[1]=num[1];//001---001 2 tree[2]=num[2]+num[1];//010---010 001 3 tree[3]=num[3];//011---011 4 tree[4]=num

HDU 1566 Color the ball(树状数组or线段树)

Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 11387    Accepted Submission(s): 5680 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"

树状数组详解(图形学算法)

目录 一.从图形学算法说起 1.Median Filter 概述 2.r pixel-Median Filter 算法 3.一维模型 4.数据结构的设计 5.树状数组华丽登场 二.细说树状数组 1.树 or 数组? 2.结点的含义 3.求和操作 4.更新操作 5.lowbit函数O(1)实现 6.小结 三.树状数组的经典模型 1.PUIQ模型 2.IUPQ模型 3.逆序模型 4.二分模型 5.再说Median Filter 6.多维树状数组模型 四.树状数组题集整理 一.从图形学算法说起 1.M

树状数组基础

关于树状数组的讲解推荐<算法竞赛入门经典训练指南> 一维版本: 洛谷3374 链接:https://www.luogu.org/problem/show?pid=3374 分析:树状数组裸的模板题 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int maxn=500000+10; 6 int c[maxn]; 7 int

BZOJ 1878 SDOI2009 HH的项链 树状数组/莫队算法

题目大意:给定一个序列.求一个区间内有多少个不同的数 正解是树状数组 将全部区间依照左端点排序 然后每次仅仅统计左端点開始的每种颜色的第一个数即可了 用树状数组维护 我写的是莫队算法 莫队明显能搞 m√m明显慢了点可是还是能接受的一个复杂度 一開始离散化数组开小了各种秒RE-- 跪了 #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algori

【BZOJ3289】Mato的文件管理 莫队算法+树状数组

[BZOJ3289]Mato的文件管理 Description Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号.为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问.Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料.Mato有一个习惯,他总是从文件大小从小到大看资料.他先把要看的文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序.排序程序可以在1单位时间内交换2个相邻的文件(因为加密

【bzoj3289】Mato的文件管理 离散化+莫队算法+树状数组

原文地址:http://www.cnblogs.com/GXZlegend/p/6805224.html 题目描述 Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号.为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问.Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料.Mato有一个习惯,他总是从文件大小从小到大看资料.他先把要看的文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序.排

经典算法题每日演练——第十题 树状数组

原文:经典算法题每日演练--第十题 树状数组 有一种数据结构是神奇的,神秘的,它展现了位运算与数组结合的神奇魅力,太牛逼的,它就是树状数组,这种数据结构不是神人是发现不了的. 一:概序 假如我现在有个需求,就是要频繁的求数组的前n项和,并且存在着数组中某些数字的频繁修改,那么我们该如何实现这样的需求?当然大家可以往 真实项目上靠一靠. ① 传统方法:根据索引修改为O(1),但是求前n项和为O(n). ②空间换时间方法:我开一个数组sum[],sum[i]=a[1]+....+a[i],那么有点意

代码与算法集锦-归并排序+树状数组+快排+深度优先搜索+01背包(动态规划)

归并排序 求逆序数 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 首先考虑下如何将将二个有序数列合并.这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数.然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可. //将有序数组a[]和b[]合并到c[]中 void MemeryArray(int a[], int n, int b[], int m, int c