c语言线段树建树程序,c语言数据结构之线段树详解;例题:校门外的树(poj2808或者vijos1448)...

线段树:它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个结点。 也就是说线段树的每一个结点对应一个区间,其中根节点对应区间[1,n] 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。 最后的叶子结点数目为N,即整个线段区间的长度。 基于二叉树编号的优秀性质,我们使用一维数组来实现线段树。

线段树支持以下操作 1、修改单点或者区间 2、查询单点或者区间 其实使用树状数组解决的问题使用线段树都可以解决,但是反过来却不行。 复杂度:假设区间长度为N,那么所有操作的复杂度都是O(logN)级别的。

一维数组模拟实现:

int id[4*N],sum[4*N],lson[4*N],rson[4*N];

一维数组以完全二叉树方式存储线段树的编程复杂度小,执行效率较高,但浪费空间。像线段树这样区间长度并不一定是 2n 的二叉树,其占用空间为 2的(最深结点的深度)次幂,就给线段树的空间占用造成了很大的不确定性。 通过证明和实践得出,使用一维数组模拟实现时一定要开到4*N!!!切记一定开四倍!!不然会死的很惨。。。

例如sum(求和),max(求最大值),min(求最小值)之类的信息,维护了一个结点的属性,具有一定的递推性质,可以在维护的时候自下而上的递推计算。 例如,把区间统一加减delta,lazy标记等信息,不仅仅代表当前结点(区间)的属性,还表示了整个子树(子区间)的属性,可以在需要进一步处理时将这种性质自上而下的传递。 线段树优秀的性质:假设修改区间【x,y】,遇到某个结点维护的区间是区间【a,b】,并且【a,b】是【x,y】的子区间,那么就在当前结点修改整个区间【a,b】的属性,不用修改到叶子结点,否则时间复杂度还不如O(n)修改。

bf5e9e8442664ad640a1930ef75f3866.png

因为线段树的性质,我们可以把结点需要维护的属性存到不同的数组中,当然,也可以每一个结点开一个结构体,把区间端点以及需要维护的性质都存到结构体中。但是为了节省存储空间,一般我们把区间端点用参数传递下去,而不用存储。

例题:

校门外的树(区间修改,区间查询)

某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。 我们可以把马路看成一个数轴,马路的一端在数轴1的位置,另一端在L的位置; 数轴上的每个整数点,即1,2,...L的位置,都种有一棵树。 由于马路上的N个区域[L1,R1],[L2,R2]...[LN,RN]要用来建地铁,区域之间可能有重合的部分。 现在要把这些区域的树(包括区域端点处的两棵树)移走。 你的任务是计算每次移走这些树后,马路上还有多少棵树。 对于100%的数据, N,L,Li,Ri <= 105。

线段树每个结点维护的属性就是:此结点所对应的区间中树的数量。 显然叶子结点初值为1,根节点初值为n。 区间修改:[L,R]区间修改。 区间查询(对应的线段树中根节点的查询):对于多次查询来说,其实就是查询根节点所维护的属性。

为了不麻烦,我们这样做

#define lson pos<<1

#define rson pos<<1|1

using namespace std;

int n,m,a[M<<2];

注:<<2的意思是乘4,<<1是乘2,<<1|1是乘2加一。

递归建树:

void build(int pos,int l,int r)

{

int mid=l+r>>1;

a[pos]=r-l+1;

if(l==r) return ;

build(lson,l,mid);

build(rson,mid+1,r);

}

递归修改:

void update(int pos,int l,int r,int x,int y)

{

int mid=l+r>>1;

if(a[pos]==0) return;

if(x<=l&&r<=y)

{

a[pos]=0;

return;

}

if(y<=mid)update(lson,l,mid,x,y);

else

{

if(x>mid)

{

update(rson,mid+1,r,x,y);

}

else

{

update(lson,l,mid,x,y);

update(rson,mid+1,r,x,y);

}

}

a[pos]=a[lson]+a[rson];//求和是这样,求最大值就是求a[lson]和a[rson]的最大值,最小值一样

}

废话不多说,源代码奉上。

#include

#include

#include

#include

#include

using namespace std;

#define M 100010

#define lson pos<<1

#define rson pos<<1|1

using namespace std;

int n,m,a[M<<2];

void build(int pos,int l,int r)

{

int mid=l+r>>1;

a[pos]=r-l+1;

if(l==r) return ;

build(lson,l,mid);

build(rson,mid+1,r);

}

void update(int pos,int l,int r,int x,int y)

{

int mid=l+r>>1;

if(a[pos]==0) return;

if(x<=l&&r<=y)

{

a[pos]=0;

return;

}

if(y<=mid)update(lson,l,mid,x,y);

else

{

if(x>mid)

{

update(rson,mid+1,r,x,y);

}

else

{

update(lson,l,mid,x,y);

update(rson,mid+1,r,x,y);

}

}

a[pos]=a[lson]+a[rson];

}

int main()

{

scanf("%d%d",&n,&m);

build(1,1,m);

for(int i=1;i<=n;i++)

{

int xd,yd;

scanf("%d%d",&xd,&yd);

update(1,1,m,xd,yd);

printf("%d\n",a[1]);

}

return 0;

}

最后说一句,切记!数组空间开四倍!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值