线段树解析(一)

一、线段树的应用场景

1.用于解决区间问题,例如求某个区间的和、最大值、最小值。

2.支持的操作有单点修改、区间修改、区间查询。

二、线段树

线段树的核心思想在于:

1.线段树的每个节点预先维护好所对应区间所需要的信息。

2.对于一次查询,将询问区间[L,R]拆分到线段树对应的节点上,通过合并这些节点已经处理好的信息快速得到答案。

3.对于一次单点修改、将其对应的叶子节点到根的所有节点信息更新。

三、线段树代码

以下代码以求区间的最小值为例

1.建树

void build(int x, int l, int r)
{
	tree[x].l = l;
	tree[x].r = r;
	if (l == r)
	{
		tree[x].value = a[l];
		return;
	}
	int mid = (l + r) / 2;
	build(2 * x, l, mid);
	build(2 * x + 1, mid + 1, r);
	tree[x].value = min(tree[2 * x].value, tree[2 * x + 1].value);
}

2.单点修改

void change(int x, int pos, int w)
{
	if (tree[x].l == pos && tree[x].r == pos)
	{
		tree[x].value = w;
	}
	int mid = (tree[x].l + tree[x].r) / 2;
	if (pos <= mid)
		change(2 * x, pos, w);
	else
		change(2 * x + 1, pos, w);
	tree[x].value= min(tree[2 * x].value, tree[2 * x + 1].value);
}

3.区间查询

int query(int x, int l, int r)
{
	if (tree[x].l == l && tree[x].r == r)
	{
		return tree[x].value;
	}
	int mid= (tree[x].l + tree[x].r) / 2;
	if (l > mid)
		return query(2 * x + 1, l, r);
	else if(r<=mid)
		return query(2 * x , l, r);
	else 
	return min(query(2 * x, l, mid),query(2*x+1,mid+1,r));
}

4.区间修改--懒标记

对于区间修改,引入懒标记的概念。在线段树的每个节点加入一个标记,代表该节点对应的区间是否修改。那么对于一次区间操作,仿照之前询问的方法,将要修改的区间[L,R]拆分到线段树的节点上,对相应节点进行整段的修改,同时更新该节点的懒标记。

void update(int x,int w)
{
	tree[x].value = w;
	tree[x].lazy_tag = w;
}
void push_down(int x)
{
	update(2 * x, tree[x].lazy_tag);
	update(2 * x+1, tree[x].lazy_tag);
	tree[x].lazy_tag = -1;
}
void push_up(int x)
{
	tree[x].value= min(tree[2 * x].value, tree[2 * x + 1].value);
}
int change(int x, int l, int r, int w)
{
	if (tree[x].l == l && tree[x].r == r)
	{
		update(x, w);
		return;
	}
	if (tree[x].lazy_tag != -1)
		push_down(x);
	int mid = (tree[x].l + tree[x].r) / 2;
	if (l > mid)
		return change(2 * x + 1, l, r,w);
	else if (r <= mid)
		return change(2 * x, l, r,w);
	else
	{
		change(2 * x, l, mid, w);
		change(2 * x + 1, mid + 1, r, w);
	};
	push_up(x);
}

5区间查询--懒标记

int query(int x, int l, int r)
{
	if (tree[x].l == l && tree[x].r == r)
	{
		return tree[x].value;
	}
    if (tree[x].lazy_tag != -1)
		push_down(x);
	int mid= (tree[x].l + tree[x].r) / 2;
	if (l > mid)
		return query(2 * x + 1, l, r);
	else if(r<=mid)
		return query(2 * x , l, r);
	else 
	return min(query(2 * x, l, mid),query(2*x+1,mid+1,r));
}

6.线段树的离散化

比如数据过大时,建立线段树无法开辟那么多单元,此时就要用到离散化了。

具体步骤如下:

1.sort(a,a+n)排序,将要用到的区间或者点集排序;

2.unique(a,a+n)去重,返回最后那个完成去重的点往后一个位置的地址,再减一个a得到去重后的数组大小;

3.lower-boud(a,a+n,x)返回a[0]-a[n-1]中第一个>=x的地址,若无,返回最末端的值,即a[n];

for(int i=0;i

 

你可能感兴趣的:(线段树解析(一))