线段树知识点理解及其模板代码


看完树状数组后, 终于发现有些地方还是需要用线段树来解题,还是 开始了解线段树了;


线段树,  真正的像一棵树了, 不想树状数组,似数不是树,较难理解, 线段树是一个平衡树,  他和树状数组有一样的功能, 包括构造线段树,区间查询,单节点更新,以及区间更新;


从比人那扒了一张图:


线段树, 有根节点,和 左右子树,  叶子节点为区间左右值, 而根节点来存 区间里要存的值, 例如:存最小值,   则 区间[0,1] 最小值是2; 2 为当前根节点;

绿色的是根节点,也就是要存的值,  白色的为叶子,也就是我们输进去的区间左右值;

线段树的精髓就是这样, 把要存的值一次一次的 存到根节点;

区间[a,b]   中间就是 (a+b)/2;


构造线段树 可以是普通数组,也可以是 结构体l,这里是结构体;

/*
线段树构造 
root:节点
begin:开始
end结束; 
*/ 
struct SegTree{
	int val;
	int addmark;
}Seg[maxn];
int array[maxn];
void build(int root,int begin,int end)
{
	if(begin==end)
		Seg[root].val=array[begin];
	else
	{
		int mid=(begin+end)>>1;
		build(root*2+1,begin,mid);//左子树
		build(root*2+2,mid+1,end);//右子树
		Seg[root].val=min(Seg[root*2+1].val,Seg[root*2+2].val);
	} 
}

构造结果为:


构造完后; 要进行查询;  查询的 思路是 从根节点开始递归查询,先左后右,一次进行, 如果查询值与区间无交集返回INF,有交集返回值 最后取最小值;

/*
区间查询 
*/
int query(int root,int begin,int end,int left,int right)
{
	if(right<begin||left>end)
		return INF;
	if(left<=begin&&right>=end)
		return Seg[root].val;
	int mid=(begin+end)>>1;
	return min(query(root*2+1,begin,mid,left,right),query(root*2+2,mid+1,end,left,right));
}

例如 查询  (3,5) 之间最小值,  应该是  3;




单节点更新,  因为 修改某一节点 会影响他的父亲节点, 因此,需要递归依次修改;


/*
单节点更新 
idex:待更新节点在array 中下标 
*/
void update_one(int root,int begin,int end,int idex,int add)
{
	if(begin==end)
	{
		if(idex==begin)
			Seg[root].val+=add;
		return;
	}
	int mid=(begin+end)>>1;
	if(idex<=mid)
		update_one(root*2+1,begin,mid,idex,add);//左更新 
	else
		update_one(root*2+2,mid+1,end,idex,add);//右更新
	Seg[root].val=min(Seg[root*2+1].val,Seg[root*2+2].val);
	
}

例如 我们在 (3,5) 之间 把 3 的值 -2; 从 4变成2;  则 (3,5) 最小值从3 变成2



区间修改:

区间修改 用到一个延迟标记的东西, 很神奇,

做法: 需要在构造数时 添加 一个标记;

区间修改时 添加pash 延时函数;

代码:

struct SegTree{
	int val;
	int addmark;
}Seg[maxn];
int array[maxn];
void build(int root,int begin,int end)
{
Seg[root].addmark=0;//为区间修改做准备¸
	if(begin==end)
		Seg[root].val=array[begin];
	else
	{
		int mid=(begin+end)>>1;
		build(root*2+1,begin,mid);//左子树
		build(root*2+2,mid+1,end);//右子树
		Seg[root].val=min(Seg[root*2+1].val,Seg[root*2+2].val);
	} 
}
void push(int root)
{
 	if(Seg[root].addmark!=0)
 	{
 		Seg[root*2+1].addmark+=Seg[root].addmark;	
 		Seg[root*2+2].addmark+=Seg[root].addmark;
 		Seg[root*2+1].val+=Seg[root].addmark;
 		Seg[root*2+2].val+=Seg[root].addmark;
 		Seg[root].addmark=0;
	}
}
int query(int root,int begin,int end,int left,int right)
{
	if(right<begin||left>end)
		return INF;
	if(left<=begin&&right>=end)
		return Seg[root].val;
push(root);//区间修改 用到延迟
	int mid=(begin+end)>>1;
	return min(query(root*2+1,begin,mid,left,right),query(root*2+2,mid+1,end,left,right));
}

/*
区间修改
*/
void update(int root,int begin,int end,int left,int right,int add)
{
	if(left>end||right<end)
		return;
	if(left<=begin&&right>=end)
	{
		Seg[root].addmark+=add;
		Seg[root].val+=add;
		return;
	}
	push(root);
	int mid=(begin+end)>>1;
	update(root*2+1,begin,mid,left,right,add);
	update(root*2+2,mid+1,end,left,right,add);
	Seg[root].val=min(Seg[root*2+1].val,Seg[root*2+2].val);
}


例如 我们将(3,5)所有的值都-2  则 有4-2=2,3-2=1;  最小值为1:




posted @ 2017-08-25 15:39  Sizaif  阅读(160)  评论(0编辑  收藏  举报