代码编织梦想

 在进行数组处理时可能会遇到以下需求:单点更改某位置的值;查询某个区间[left, right]内的值的和,如果普通地进行遍历数组,则时间复杂度可能达到O(n^2),为了降低时间复杂度,可以使用树状数组来实现单点修改和区间求和

lowbit()函数原理及作用

在正式学习树状数组之前,需要介绍一下lowbit()函数。

lowbit函数的写法如上,他的作用是得到非负整数 i 在二进制表示下,最低位的 1 与其后面的所有 0 组成的数值。

e.g.  lowbit(8)=lowbit(100)(100是二进制表示下的8)= 8;

lowbit (9)=lowbit (101) (101是二进制表示下的9)=1;

lowbit(44)=lowbit(101100)(101100是二进制表示下的44)=8;

而这一操作实际上可以通过以下方式得到:对原数字取反加一。以 i=44 为例,对101100取反得到010011,加一得到010100.这时我们发现,蓝色数字和紫色数字如果按位与,则得到的数是00100,正是lowbit(44)的结果。我们又知道,计算机存储的数是补码形式,而 ~i+1=  -i(~是取反符号,这个式子意为: -i 就是对 i 进行取反加一操作),并且一个数取反加一两次后得到的仍是原数,所以lowbit(i)=(-i)& i 。

那么为什么要引入lowbit函数呢?观察一下

(图源树状数组,就是这么简单!_哔哩哔哩_bilibili) 

这种图片里的c [ ]数组(实际上就是树状数组),将每个数组里的数字以二进制数表示,类似下图。

(图源树状数组,就是这么简单!_哔哩哔哩_bilibili) 

 可以看到,对树状数组里的序号数取lowbit值后得到的就是该数组元素里存放的数据个数。例如: t [1]里有一个数据,lowbit(1)==1.t [2]里有2个数据,lowbit(2)==2.t [3]里有1个数据,lowbit(3)==1……因此lowbit函数可以计算树状数组中某个元素包含几个数据。

应用:

 c [6]的直接前驱是c [4],可以表示为c [ 6 - lowbit(6) ],c [6]的直接后继是c [8],可以表示为c [6 + lowbit(6) ],通过这种计算方法,可以便捷地实现单点修改和区间求和功能。

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=10005;
int n,a[maxn],c[maxn];

int lowbit(int i){//c[i]的区间长度 
	return (-i)&i;
}

void add(int i,int z)//点更新,a[i]加上z
{	 
	/*while(i+lowbit(i)<=n)
	{
		c[i]+=z;
		i+=lowbit(i);
	}*/
	for(;i<=n;i+=lowbit(i))
	c[i]+=z;
}

int sum(int i)//前缀和,a[1]+a[2]+...+a[i]
{
	int sum=0;
	for(;i>0;i-=lowbit(i))
	sum+=c[i];
	return sum;
}

int sum(int i,int j)//区间和,a[i]+a[i+1]+...+a[j]
{
	/*int sum=0;
	for(;j>=i;j-=lowbit(j))
	sum+=c[j];
	return sum;*/
	return sum(j)-sum(i-1);
}

需要注意的是,单点修改时,不仅需要修改指定的点,还需要将该点的所有后继都进行修改,代码见上面的 add()函数。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_74758366/article/details/128770168

树状数组浅谈 hdu 1166 敌兵布阵-爱代码爱编程

    这一块在寒假前就已经理解了,开学后再看的时候感觉有点生疏了,感觉还是在这总结一下比较好。 一维树状数组: 首先理解一个函数int lowbit( int x),这个函数是求从下标x-(x&(-x))+1开始计数,连续相加到下标x处,总共有x&(-x)个数相加。一维树状数组就是要求给你一个数组序列,以各个元素为叶子节点,从下往上构造

浅谈——树状数组-爱代码爱编程

树状数组 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 +

树状数组是个啥?浅谈树状数组(一)-爱代码爱编程

树状数组 有问题欢迎在评论区讨论! 问题引入 题目链接:HDU1166 n个数字,m次操作。 操作分别为: Query i j (查询区间 [i, j] 的和)Add i j (第i个数字增加j)Sub i j (第i个数字减去j) 使用树状数组 为了简单且高效地解决这个问题,我们需要使用树状数组(当然也可以用线段树)。 线

leetcode 刷题系列 -- 143. 重排链表-爱代码爱编程

给定一个单链表 L 的头节点 head ,单链表 L 表示为: L0 → L1 → … → Ln - 1 → Ln 请将其重新排列后变为: L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 示例 1:

力扣hot100刷题笔记——其他类型-爱代码爱编程

其他类型题目 78. 子集   题目概述:给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 class Sol

【数据结构】二叉树-爱代码爱编程

1.树概念及结构 1.1树的概念 树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。 有一个特殊的结点,称为根结点,根节点没有前驱结点 除根节点外,其余结点被分成M(

java链表oj题-爱代码爱编程

目录 1. 删除链表中等于给定值val的所有结点2. 逆置单链表3. 链表的中间结点4. 链表中倒数第k个结点5. 将两个有序链表合并为一个新的有序链表6. 以给定值x为基准将链表分割成两部分7. 判断是否为回文链表

【数据结构之二叉树系列】万字深剖普通二叉树的遍历+分治算法思想-爱代码爱编程

目录 前言一、背景知识二、前序遍历三、中序遍历四、后序遍历五、求二叉树中结点的个数1. 遍历+计数(1)前序遍历+计数(2)中序遍历+计数(3)后序遍历+计数 2.分治算法思想(推荐) 六、求叶子结点个

数据结构---堆-爱代码爱编程

堆 ·定义 ·基本操作 ·建堆 ·堆排序 ·优先队列 一、堆的定义: ·堆必须是一个完全二叉树 ·还得满足堆序性 什么是完全二叉树呢? ·完全二叉树只允许最后一行不为满 ·且最后一行必须从左到右排序 ·最后一行元素之间

【数据结构】8.3 交换排序-爱代码爱编程

文章目录 1. 冒泡排序冒泡排序算法冒泡排序算法分析 2. 快速排序快速排序算法快速排序算法分析 基本思想 每两个元素之间互相比较,如果发现大小关系相反,则将他们交换过来,直到所有记录都排好序为止。假

c++11 并发指南六(atomic 类型详解三 stdatomic (续))-爱代码爱编程

C++11 并发指南六(atomic 类型详解三 std::atomic (续)) C++11 并发指南六( 类型详解二 std::atomic ) 介绍了基本的原子类型 std::atomic 的用法,本节我会给大

python中的迭代器与生成器-爱代码爱编程

Python中的迭代器与生成器 在Python中存在两种好用的功能:迭代器与生成器。以list容器为例,在使用该容器迭代一组数据时,必须事先将所有数据存储到容器中,才能开始迭代;而生成器却不同,它可以实现在迭代的同时生成元素。 也就是说,对于可以用某种算法推算得到的多个数据,生成器并不会一次性生成它们,而是什么时候需要,才什么时候生成。 迭代器

超详解线段树(浅显易懂)-爱代码爱编程

一,什么是线段树? 线段树是怎样的树形结构?   线段树是一种二叉搜索树,而二叉搜索树,首先满足二叉树,即每个结点最多有两颗子树,并且是一颗搜索树,我们要知道,线段树的每个结点都存储了一个区间,也可以理解成一个线段,而搜索,就是在这些线段上进行搜索操作得到你想要的答案。 线段树能够解决什么样的问题?   线段树的适用范围很广,可以在线维

08-爱代码爱编程

目录 列表和列表项的简介 列表 列表项  迷你列表项 列表和列表项的关系  列表相关API函数介绍  初始化列表vListInitialise函数详解 列表项的初始化函数vListInitialiseItem函数 列表项的插入vListInsert函数 列表项末尾插入vListInsertEnd函数 列表项的删除函数uxListRem

浅谈树状数组 区间修改 区间查询_树状数组区间修改区间查询_q779的博客-爱代码爱编程

一、区间修改,单点查询 首先我们可以先来想一下,树状数组的区间修改,单点查询怎么弄 我们可以维护一个关于原数组的差分数组 很容易知道