这次的训练日记主要是关于线段树的,线段树的本质是一棵二叉树,线段树的每一个节点记录的是一段区间的信息。线段树类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)!
虽然线段树的时间复杂度比较低,但是其算法需要的空间很大,线段树需要的空间为数组大小的四倍!
其主要用来解决区间极值、区间求和等问题,当然就包括区间更新后求极值、求和等问题。主要解决的问题有单点更新、成段更新、区间合并、扫描线等等。
其中区间更新后问题可分为以下三种:
①更新点,查询区间
②更新区间,查询点
③更新区间,查询区间
需要的操作主要有建立树、更新、查询等操作,其中需要的求和操作或求极值操作需要都包括在这几种操作的过程中。
这几天看了课件里的例题,线段树的模版代码还是有好几种的,但是核心思想是完全一样的。比较了几种模版代码,我还是感觉ppt中的用结构体储存相关信息的模版比较顺手。
其模版如下:
struct Tree
{
int left,right; //区间的端点
int max,sum; //max用于求最大值 sum用于求和(视题目要求而定)
};
//建立树
void build(int id,int l,int r)
//id为节点,l(left)为区间左端点,r(right)为区间右端点(下同)
{
tree[id].left=l;
tree[id].right=r;
if (l==r)
{
tree[id].sum=a[l];
tree[id].max=a[l];
}
else
{
int mid=(l+r)/2;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
tree[id].max=max(tree[id*2].max,tree[id*2+1].max;
}
}//如果原数组从a[1]~a[n],调用build(1,1,n)即可
//更新
void update(int id,int pos,int val)
//pos为要更新数据的位置 val为要更新为的值
{
if (tree[id].left==tree[id].right)
{
tree[id].sum=tree[id].max=val;
}
else
{
int mid=(tree[id].left+tree[id].right)/2;
if (pos<=mid) update(id*2,pos,val);
else update(id*2+1,pos,val);
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
tree[id].max=max(tree[id*2].max,tree[id*2+1].max)
}
}
//若更新a[k]的值为val,调用update(1,k,val)即可
//查询
void query(int id,int l,int r)
{
if (tree[id].left==l&&tree[id].right==r)
return tree[id].sum; //询问总和
else
{
int mid=(tree[id].left+tree[id].right)/2;
if (r<=mid) return query(id*2,l,r);
else if (l>mid) return query(id*2+1,l,r)
else
return query(id*2,l,mid)+query(id*2+1,mid+1,r);
}
}//调用query(1,l,r)即可查询[l,r]区间内元素的总和
由于刚开学的原因,乱七八糟的事情比较多,而且课也不少,并没有太多时间看太多有关线段树的相关例题,只看了课件里的一部分例题,还没有全部研究完。在这周接下来的几天里争取把课件里以及那个博客里的例题看个差不多。
继续加油!