线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。
因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
用线段树解题关键是要清楚的知道每个节点要存放什么信息,以及这些节点如何高效的更新,维护,查询,不一定每次都更新到叶子节点,这样复杂度最坏有可能是0(n)
下面附上一道题
poj3264
思路:用一维数组存放节点,节点数<=4n-1,每个节点存放该区间的最大值,最小值
代码
#include<iostream>
#include<stdio.h>
using namespace std;
const int INF=100000000;
int minV=INF;
int maxV=-INF;
int max(int a,int b)
{
return a>b?a:b;
}
int min(int a,int b)
{
return a>b?b:a;
}
//节点信息
struct Note
{
int L,R;
int minV,maxV;
int Mid()
{
return (L+R)/2;
}
};Note tree[800010];
//建树
void buildtree(int root,int L,int R)
{
tree[root].L=L;
tree[root].R=R;
tree[root].minV=INF;
tree[root].maxV=-INF;
if(L!=R)
{
buildtree(root*2+1,L,(L+R)/2);
buildtree(root*2+2,(L+R)/2+1,R);
}
}
//插入
void insert(int root,int i,int v)
{
if(tree[root].L==tree[root].R)
{
tree[root].minV=tree[root].maxV=v;
return ;
}
tree[root].minV=min(v,tree[root].minV);
tree[root].maxV=max(v,tree[root].maxV);
if(i<=tree[root].Mid())
{
insert(2*root+1,i,v);
}
else
{
insert(2*root+2,i,v);
}
}
//查询
void chaxun(int root,int s,int e)
{
if(tree[root].minV>=minV&&tree[root].maxV<=maxV)
{
return ;
}
if(tree[root].L==s&&tree[root].R==e)
{
minV=min(minV,tree[root].minV);
maxV=max(maxV,tree[root].maxV);
return ;
}
if(e<=tree[root].Mid())
{
chaxun(2*root+1,s,e);
}
else if(s>tree[root].Mid())
{
chaxun(2*root+2,s,e);
}
else
{
chaxun(2*root+1,s,tree[root].Mid());
chaxun(2*root+2,tree[root].Mid()+1,e);
}
}
int main()
{
int p,n;
int i,h;
while(scanf("%d%d",&n,&p)!=-1)
{
buildtree(0,1,n);
for(i=1;i<=n;i++)
{
scanf("%d",&h);
insert(0,i,h);
}
for(i=0;i<p;i++)
{
int s,e;
scanf("%d%d",&s,&e);
minV=INF;
maxV=-INF;
chaxun(0,s,e);
printf("%d\n",maxV-minV);
}
}
return 0;
}