数据结构——线段树

线段树
一.概念
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。(来自百度)
二.代码分段解析
代码中涉及许多左移和右移操作,“>>1”="/2","<<1"="*2",区别就是速度比一般的乘除更快
1.定义

using namespace std;
struct tree{
   ll sum;
   ll lazy;//懒标记
}node[maxn<<2];//=[maxn*4],上面提到过数组开4N以免越界

一般用结构体来定义,lazy标记主要用来标记运算过程
2.构造树

void build(int rt,int l,int r)
{
	node[rt].lazy=0;
    if(l==r)
    {
		node[rt].sum=a[l];//找到更新的点,给该点赋上初值
    	return ;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);//递归继续构造左子树
    build(rt<<1|1,mid+1,r);//右子树
    update(rt);//更新当前节点
}

3.更新

void update(int rt)
{
	node[rt].sum=node[rt<<1].sum+node[rt<<1|1].sum;
}

这只是求和的一种更新方式,不同题需要不同的更新
如找最大值:

void update(int rt)
{
	node[rt].maxx=max(node[rt<<1].maxx,node[rt<<1|1].maxx);
}

4.改变
(1)区间修改

void change(int rt,int l,int r,int pl,int pr,ll val)
//根节点,区间最左端和最右端,要改变的区间最左端和最右端,改变的值
{
	if(pl==l&&pr==r)//找到完全重合的区间
	{
		node[rt].sum+=val*(pr-pl+1);
		node[rt].lazy+=val;//将已经改变的区间进行懒标记,但在往下推后要把这个懒标记去除
		return ;
	}
	pushdown(rt,l,r);
	int mid=(r+l)>>1;
	if(pr<=mid)
		change(rt<<1,l,mid,pl,pr,val);//区间全在左子树
	else if(pl>mid)
		change(rt<<1|1,mid+1,r,pl,pr,val);//区间全在右子树
	else//区间在左子树,右子树都有
	{
		change(rt<<1,l,mid,pl,mid,val);
		change(rt<<1|1,mid+1,r,mid+1,pr,val);
	}
	update(rt);//更新当前结点
}

5.下推lazy标记

void pushdown(int rt,int l,int r)
{
	if(node[rt].lazy==0)
		return;
	int chl=rt<<1;
	int chr=rt<<1|1;//=rt*2+1
	int mid=(l+r)>>1;
	node[chl].sum+=node[rt].lazy*(mid-l+1);//给rt的左子节点重新赋值
	node[chr].sum+=node[rt].lazy*(r-mid);//给rt的右子节点重新赋值
	node[chl].lazy+=node[rt].lazy;//将lazy标记下传到子节点
	node[chr].lazy+=node[rt].lazy;
	node[rt].lazy=0;//往下推后要去掉当前节点lazy标记
}

6.询问

ll query(int rt,int l,int r,int pl,int pr)
{
	if(l==pl&&r==pr)//找到查询区间,如果区间完全重合,返回
		return node[rt].sum;
	pushdown(rt,l,r);//没有返回就继续往下找
	int mid=(l+r)>>1;
	if(pr<=mid)
        return query(rt<<1,l,mid,pl,pr);//查询区间全在左子树上
	else if(pl>mid)
		return query(rt<<1|1,mid+1,r,pl,pr);//查询区间全在右子树上
    else
		return query(rt<<1,l,mid,pl,mid)+query(rt<<1|1,mid+1,r,mid+1,pr);
}

如果代码还没理解透彻,可以自己跟着模拟看看(画个树状图,列个表格),看看代码是怎么实现的
自己也还在学习,持续更新中……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值