[note]线段树学习笔记

自从初中学习了就再也没用过,上了大学发现全忘光了,特此重新学习记录学习笔记。

学习参考:https://www.cnblogs.com/fusiwei/p/11301700.html

首先明确线段树的作用:用于区间统计,统计区间的某一特性,这一特性可以是区间求和,区间最值。

线段树是二叉树结构,一个节点代表的是一个区间,根节点1是全部区间

假设父节点为x,则左儿子为2x,右儿子为2x+1

线段树可以进行非常多操作,以及拓展衍生出的多种用法,这里先暂时只学习简单线段树的用法:

简单线段树支持:单点查询、单点修改、区间查询、区间修改

相比于树状数组,线段树更强大,树状数组可以解决的问题线段树都可以解决,但是线段树可以解决的问题树状数组不一定可以解决,但是,树状数组的代码复杂度与常数较低,所以在写代码的过程中要平衡考虑,不单一看待。

实现线段树:

有两种,一种是结构体方式,维护节点的编号、节点所表示的区间,用一维数组实现的。

线段树的时间复杂度是O ( l o g n )

模板1:简单线段树(实现区间修改加值、区间查询数值和)

一般来说,区间总长度为n,线段树空间开 4n 为宜。

建树

const int N = 1e5+5;

#define ls (pos<<1)
#define rs (pos<<1|1)

int Tree[4*N];

void build_tree(int pos,int l,int r)//pos是数组下标,l,r是区间
{
	if(l==r){//叶子节点
		Tree[pos] = A[l];//赋值
		return ;
	}
	int mid = (l+r)>>1;
	build_tree(ls,l,mid);
	build_tree(rs,mid+1,r);//分裂区间,向下建树
	Tree[pos] = Tree[ls]+Tree[rs];//向上更新
	return ;
}

当程序不给出初始值时,自然不需要建树操作。

区间修改

lazy标记的理解及其pushdown操作:

为何叫lazy?如果我们需要修改区间[x,y]内容,而当前递归的点表示的区间[a,b]是[x,y]的子区间,则我们不用继续往下深入递归,而是直接修改该点,放下lazy标记修改整个区间的属性(这也就是为什么线段树快的原因,如果递归到叶子节点,时间复杂度比O(n)还大)。先对线段树该点加上修改的区间长度*修改值(也就是对这个点区间加上全部的值),再放下一个lazy标记,加上修改的值。

为什么pushdown?因为如果只修改大区间,小区间并没有得到修改,所以需要在我们后面递归往下的时候进行pushdown操作。pushdown的操作就是线段树提高效率的核心!

void mark(int pos,int l,int r,int k)
{
	Tree[pos] +=(r-l+1) * k;//大区间增加区间总值
	lazy[pos] +=k;//lazy标记
	return ;
	
}

void push_down(int pos,int l,int r)
{
	int mid = (l+r)>>1;
	mark(ls,l,mid,lazy[pos]);
	mark(rs,mid+1,r,lazy[pos]);//向下增加积累的lazy值
	lazy[pos] = 0;//清空lazy标记
}

void update(int pos,int l,int r,int x,int y,int k)
{
	//在x-y区间增加k
	if(x<=l&&r<=y){
		mark(pos,l,r,k);
		return ;
	}
	int mid = (l+r)>>1;
	push_down(pos,l,r);//向下递归前要释放lazy标记
	if(x<=mid)
		update(ls,l,mid,x,y,k);
	if(y>mid)
		update(rs,mid+1,r,x,y,k);
	Tree[pos] = Tree[ls] + Tree[rs];//更新
	return ;
}

查询区间

与修改的思路保持一致,不断递归返回子树在所求区间范围的值。

int Query(int pos,int l,int r,int x,int y)
{
	int ret = 0;
	if(x<=l&&r<=y) return Tree[pos];
	int mid = (l+r)>>1;
	if(x<=mid)
		ret += Query(ls,l,mid,x,y);
	if(y>mid)
		ret += Query(rs,mid+1,r,x,y);
	return ret;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值