线段树详解

##索引(Index)
在求一段区间的值,比如说求区间最大值,或要在某一段区间修改他们的值,那么我们就可以使用线段树(segment tree)实现。这种数据结构在大型OI比赛中常考,所以说掌握了线段树就可以在OI比赛中得到某题的高分。同时,线段树在日常生活中很实用,是一种算法的基础。
##介绍(Introduction)
线段树,英文segment tree,顾名思义,它是一棵树,是一棵满二叉树。树上的每一个节点都代表着一段区间。线段树数据结构思想:二分思想(binary search)。首先根节点是整个区间,然后每一个节点的左儿子代表父节点区间的左半区间,右儿子代表父节点区间的右半区间,如下图。
这里写图片描述
我们可以用这棵树维护区间的很多东西,如最大值,区间和等。
时间复杂度: O ( l o g n ) O(log_n) O(logn)
##操作(Operations)
###建造线段树(Build a segment tree)
方法:递归(recursion)。
维护两个指针l,r,如果l=r,那么就返回需要的值;否则将区间[l,r]分成两半继续递归。
####伪代码

void build(线段树下标,指针)
{
    如果区间大小为1(即l,r指针相同) 记录值
    build(leftson);
    build(rightson);
    tree[下标]=max(tree[leftson],tree[rightson]);//假设现在要求的是区间最大值
}

####代码(Code)

void build(int x,int l,int r)//假设现在要求的是区间最大值
{
    if (l==r)
    {
        tree[x]=a[l];
        return;
    }
    int wz=(l+r)/2;
    build(x*2,l,wz);
    build(x*2+1,wz+1,r);
    tree[x]=max(tree[x*2],tree[x*2+1]);
}

###修改操作(Modify)
递归下去,如果发现指针一样,那么就修改,否则将区间分成两份,继续递归。
####代码(Code)

void modify(int x,int l,int r,int xb,int val)
{
    if (l==r)
    {
        tree[x]=val;
        return;
    }
    int wz=(l+r)/2;
    if (xb<=wz) modify(x*2,l,wz,xb,val);
           else modify(x*2+1,wz+1,r,xb,val);
    tree[x]=max(tree[x*2],tree[x*2+1]);
}

###查找操作(Find)
跟上面一样,找到了就退,没找到就将区间分成两份,继续递归。
####代码(Code)

int find(int x,int l,int r,int xb)
{
    if (l==r)
    {
        return max(find,tree[x]);
    }
    int wz=(l+r)/2;
    if (xb<=wz) find(x*2,l,wz,xb);
           else find(x*2+1,wz+1,r,xb);
}

##难点(Special difficulties)
如果修改的不是一个位置,而是一段区间,那又怎么办?
我们回去看到上面介绍线段树的那幅图,如果要求2-3区间的最大值,怎么办?无非就是求2-2和3-3的最大值吗?是的。
怎么实现不用暴力修改区间?
没事!再让tree数组开多一维,tree[1]记录值,tree[2]记录修改的数,这样防止在搜索更小的区间时出现错误,从而实现不用暴力修改区间。
###实现方法(The way to achieve)
区间修改:在二分区间的时候顺便将要修改的区间也拆分掉(具体见下面的代码)。
Lazy标志:在修改的时候将tree[1]和tree[2]修改为要修改的数,然后后面递归的时候将tree[2]的值记录到tree[leftson][2],tree[rightson][2]里面,实现不用暴力修改区间。
##代码(Code)

void modify(int x,int l,int r,int l1,int r1,int val)
{
    if (l==r)
    {
        tree[x][1]=tree[x][2]=val;
        return;
    }
    if (tree[s][2]!=-1)
	{
		tree[s*2][1]=tree[s][1];
    	tree[s*2][2]=tree[s][2];
    	tree[s*2+1][1]=tree[s][1];
    	tree[s*2+1][2]=tree[s][2];
    	tree[s][2]=-1;  //此时表示大区间修改完了。
	}
    int wz=(l+r)/2;
    if (r1<=wz) modify(x*2,l,wz,l1,r1,val);
	    else if (l1>wz) modify(x*2+1,wz+1,r,l1,r1,val);
	         else
	         {
	              modify(x*2,l,wz,l1,wz,val);
	        	  modify(x*2+1,wz+1,r,wz+1,r1,val);
			 }
    tree[x][1]=max(tree[x*2][1],tree[x*2+1][1]);
}

##总结(Summaries)
如果题目问到要求区间或是什么的,就要想到线段树,线段树的代码其实很好打,不要因为怕长就放弃,想暴力。如果理解了,就马上学会了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值