一、什么是线段树?
线段树就是把区间逐次二分得到的一种树状结构,所以它是一颗二叉树。
线段树适用于解决序列上的动态问题,经典问题如:动态区间查询统计问题,包括:区间查询(区间和、区间最值、最大公约、最小公倍等查询问题)、单点查询、单点修改、区间修改、区间K值查询等问题。
二、线段树的两种建树方式
1. 自顶向下构建线段树
(1)说明:完全根据定义,根结点为最大区间[0,n-1],向下不断二分区间,直到叶子结点[i,i],便可得到整颗线段树,建树过程有着很好的递归结构,一般使用递归建树。如图所示,以长度为8的序列构建一颗线段树,区间划分如下:
(2) 便可以得到代码,以区间和为例(C++)
#define MAX_N (int)(1e7+1)
#define l(x) (x<<1)
#define r(x) (x<<1|1)
struct node{
int val;
};
node dat[MAX_N];//dat[0]不存数 ,结点dat[1]为树根
/*自顶向下建树 O(nlogn)
i:当前结点在线段树中的编号为i
[l,r]:当前结点所维护的区间
*/
void build(int i,int l,int r){
if(l==r){//叶子节点
cin>>dat[i].val;
return;
}
int mid=(l+r)>>1;
build(l(i),l,mid);//递归构建左子数
build(r(i),mid+1,r);//递归构建右子树
dat[i].val=dat[l(i)].val+dat[r(i)].val;//区间和
}
2.自底向上构建线段树
(1)说明:自底向上建树,并没有把区间真正地二分(叶子结点数目为2的幂的情况下才是完美二分),它只是应用了线段树中父结点与两个子结点的位置关系。它是通过提前建好一个二叉树,再将待处理序列元素送到该二叉树的最底层构成叶子结点,然后自底向上一层一层地根据父结点与两个子结点间的关系,为每个结点填充值,直达根点。自底向上一般使用非递归建树,直接向结点中填充值即可。
(2)代码以区间和为例
#define MAX_N (int)(1e3+1)
//左孩子
#define l(x) (x<<1)
//右孩子
#define r(x) (x<<1|1)
int n,dat[MAX_N];
/*
初始化
N:为序列元素个数
n:大于等于N的第一个2的幂
*/
void init(int N){
for(n=1;n<N;n<<=1);//n=log2N
memset(dat,0,sizeof(dat));
}
//自底向上建树
void build(int N){
for(int i=n;i<n+N;++i) cin>>dat[i];//最底层叶子结点存放输入序列
//根据左右孩子节点自底向上构建父节点
for(int i=n-1;i>=0;--i) dat[k]=dat[l(k)]+dat[r(k)];//维护区间和
}
来源:CSDN
作者:Hold the time
链接:https://blog.csdn.net/qq_42323056/article/details/104440701