征服线段树之线段树的两种建树方式详解——自顶向下建树与自底向上建树 C/C++代码

时光怂恿深爱的人放手 提交于 2020-02-22 13:38:14

一、什么是线段树?

线段树就是把区间逐次二分得到的一种树状结构,所以它是一颗二叉树。

线段树适用于解决序列上的动态问题,经典问题如:动态区间查询统计问题,包括:区间查询(区间和、区间最值、最大公约、最小公倍等查询问题)、单点查询、单点修改、区间修改、区间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)];//维护区间和 
}

 

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