关于线段树和树状数组的一些知识集合

我们两清 提交于 2019-11-30 05:48:05

线段树:用于降低时间复杂度的算法

这是昨天大佬讲的一些算法,我自己整理了一些,顺序有点凌乱,下面还有一些网上大佬们的资料,可以当作消遣看一看。
今天也是努力学习的菜鸡啊QAQ
个人觉得线段树这里还是不难理解的,只是我还不会代码实现(哭辽?)
菜鸡要努力啊!冲鸭!
下面是正文部分:

*特点:

  1. 有根数:根是确定树中一个点作为根 靠近根的为“父” 远离根的为“子”。
  2. 二叉树 只有两个分支的树 分为左分支和右分支 有序的
  3. 每个节点可以有不同的含义
    (例如:可以代表一个区间的和,最大值,最小值等)可以存在标记(标记下放*不太懂)
    若节点为(a,b) 左节点(a,(a+b)/2) 右节点((a+b)/2+1,b)

线段树主要处理区间上的问题

  1. 每层最多有两个终止节点(*)
  2. 标号的概念:即存储每个节点数据的数组下标
    *3.标记下放
    EX:求Ai-Aj 的和 其中每个点都加n (标记在节点中存储)

树状数组

重要代码实现;(c++)

Using namespace std;
Void xg(int x)
{
While(x<=n);
{
A[x]++;
X+=x&(-x);(x累加x的lowbit)
}
}

(以上是自我笔记)
网上资料:
给定区间【L,R】,如何分解成上述给定的区间?
对于给定区间[2,12]要如何分解成上述区间呢?

分解方法一:自下而上合并——利于理解
先考虑树的最下层,将所有在区间[2,12]内的点选中,然后,若相邻的点的直接父节点是同一个,那么就用这个父节点代替这两个节点(父节点在上一层)。这样操作之后,本层最多剩下两个节点。若最左侧被选中的节点是它父节点的右子树,那么这个节点会被剩下。若最右侧被选中的节点是它的父节点的左子树,那么这个节点会被剩下。中间的所有节点都被父节点取代。对最下层处理完之后,考虑它的上一层,继续进行同样的处理。

*** 怎么用数组来表示一颗二叉树呢?假设某个节点的编号为v,那么它的左子节点编号为2v,右子节点编号为2v+1。
然后规定根节点为1.这样一颗二叉树就构造完成了。通常2v在代码中写成 v<<1 。 2v+1写成 v<<1|1 。
线段树代码实现:

定义//
#define maxn 100007
int A[maxn],n,N;//原数组,n为原数组元素个数 ,N为扩充元素个数
int Sum[maxn<<2];//区间和
int Add[maxn<<2];//懒惰标记

建树:

void Build(int n){
//计算N的值
N=1;while(N < n+2) N <<= 1;
//更新叶节点
for(int i=1;i<=n;++i)
Sum[N+i]=A[i];//原数组下标+N=存储下标
//更新非叶节点
for(int i=N-1;i>0;–i)
{
//更新所有非叶节点的统计信息
Sum[i]=Sum[i<<1]+Sum[i<<1|1];
//清空所有非叶节点的Add标记
Add[i]=0;
}
} ????

//PushUp函数更新节点信息,这里是求和

void PushUp(int rt){Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];} (求每个节点的数据)
//Build函数建立线段树
void Build(int l,int r,int rt){ //[l,r]表示当前节点区间,rt表示当前节点的实际存储位置
if(lr) {//若到达叶节点 (到达最低点)
Sum[rt]=A[l];//存储A数组的值
return;
}
int m=(l+r)>>1(求中间值);
//左右递归
Build(l,m,rt<<1); (建立左分支)
Build(m+1,r,rt<<1|1); (建立右分支)
//更新信息
PushUp(rt);
}
功能:点修改
void Update(int L,int C,int l,int r,int rt){//[l,r]表示当前区间,rt是当前节点编号//l,r表示当前节点区间,rt表示当前节点编号
if(l
r){//到达叶节点,修改叶节点的值
Sum[rt]+=C;
return;
}
int m=(l+r)>>1;
//根据条件判断往左子树调用还是往右
if(L <= m) Update(L,C,l,m,rt<<1);
else Update(L,C,m+1,r,rt<<1|1);
PushUp(rt);//子节点的信息更新了,所以本节点也要更新信息
}

功能:区间查询

int Query(int L,int R,int l,int r,int rt){//[L,R]表示操作区间,[l,r]表示当前区间,rt:当前节点编号
if(L <= l && r <= R){
//在区间内直接返回
return Sum[rt];
}
int m=(l+r)>>1;
//左子区间:[l,m] 右子区间:[m+1,r] 求和区间:[L,R]
//累加答案
int ANS=0;
if(L <= m) ANS+=Query(L,R,l,m,rt<<1);//左子区间与[L,R]有重叠,递归
if(R > m) ANS+=Query(L,R,m+1,r,rt<<1|1); //右子区间与[L,R]有重叠,递归
return ANS;
}
树状数组:

C(2)=1对应二进制代码+lowbit(1);
C(4)=2对应二进制代码+lowbit(2)其他同

代码实现:

int lowbit(int x){//求和范围/
return x & (x ^ (x - 1));(-x)
}
void buildtree(){//建立c数组/
for (int i = 1; i <= n; ++i){
int l = lowbit(i);
for (int j = i - l + 1; j <= i; ++j)
c[i](建立的树状数组) += aj;
}
}
void add(int k, int d){//修改:给k加上d/
if (k > n) return ;
c[k] += d;
add(k + lowbit(k), d);
}
int sum(int s, int t){//求区间s~t的和/
int ans1 = 0, ans2 = 0;
for (int i = t; i >= 1; i -= lowbit(i)) ans1 += c[i];
for (int i = s - 1; i >= 1; i -= lowbit(i)) ans2 += c[i];
return ans1 - ans2;
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!