一、定义
- 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
- 线段树的每一个结点都保存一条线段(即一个区间),对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
特性:
- 时间复杂度:建树操作O(N),查询、更新操作O(logN);此外(未优化)需要额外的空间O(4*N)
- 处理的问题通常具有区间合并性,即一个区间(结点)的值由其子区间(其子结点)综合得出,如查询区间最大值/最小值等问题。
- 采用了二分的思想方法,原数组被划分为单元区间存储在线段树的叶子结点中。
- 因为线段树是完全二叉树,所以可以用数组来存放,注意数组大小至少要开到4*N。
二、线段树基本操作
以下均以查询区间内最大值为例
1. 建树
int N,a[maxn],tree[4*maxn];
void build(int root,int l,int r) //l:当前区间左边界 r:当前区间右边界
{
if(l==r) //当l==r,说明该结点为叶子结点(单元区间)
{
tree[root]=a[l];
return; //返回
}
int mid=(l+r)/2; //将当前区间划分为左右两个区间
build(root*2,l,mid); //向左子树递归
build(root*2+1,mid+1,r); //向右子树递归
tree[root]=max(tree[root*2],tree[root*2+1]); //回溯(合并区间)
}
2. 单节点更新
void updata(int root,int l,int r,int x,int num) //x:更新结点在原数组中的位置 num:新值
{
if(l==r) //此时l==r==x,找到了位置(对应的叶子结点)
{
tree[root]=a[l]=num; //该位置和该叶子结点更新为新值
return; //返回
}
int mid=(l+r)/2;
if(x<=mid) //x在左子树(左区间)
updata(root*2,l,mid,x,num);
else //x在右子树(右区间)
updata(root*2+1,mid+1,r,x,num);
tree[root]=max(tree[root*2],tree[root*2+1]); //回溯(合并区间)
}
3. 区间查询
int query(int root,int l,int r,int ql,int qr) //ql:查询区间左边界 qr:查询区间右边界
{
if(ql<=l&&r<=qr) //当前区间在查询区间内,直接返回该结点(区间)值
return tree[root];
if(r<ql||l>qr) //当前区间与查询区间不相交
return INT_MIN; //返回一个足够小的值,从而保证回溯时不会被选择
int mid=(l+r)/2;
return max(query(root*2,l,mid,ql,qr),query(root*2+1,mid+1,r,ql,qr));
}
三、模板例题
- HDU - 1754 (该题有多组输入…
AC代码:
#include<cstdio>
#include<climits>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=2e5+10;
int N,a[maxn],tree[4*maxn];
void build(int root,int l,int r)
{
if(l==r)
{
tree[root]=a[l];
return;
}
int mid=(l+r)/2;
build(root*2,l,mid);
build(root*2+1,mid+1,r);
tree[root]=max(tree[root*2],tree[root*2+1]);
}
void updata(int root,int l,int r,int x,int num)
{
if(l==r)
{
tree[root]=a[l]=num;
return;
}
int mid=(l+r)/2;
if(x<=mid)
updata(root*2,l,mid,x,num);
else
updata(root*2+1,mid+1,r,x,num);
tree[root]=max(tree[root*2],tree[root*2+1]);
}
int query(int root,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
return tree[root];
if(r<ql||l>qr)
return INT_MIN;
int mid=(l+r)/2;
return max(query(root*2,l,mid,ql,qr),query(root*2+1,mid+1,r,ql,qr));
}
int main()
{
int M;
while(~scanf("%d %d",&N,&M))
{
for(int i=1;i<=N;i++)
scanf("%d",&a[i]);
build(1,1,N);
while(M--)
{
getchar();
char C=getchar();
int A,B;
scanf("%d %d",&A,&B);
if(C=='Q')
printf("%d\n",query(1,1,N,A,B));
else
updata(1,1,N,A,B);
}
}
return 0;
}