对于线段树,那说明是由线段与树结合起来考虑!线段树是建立在线段的基础之上,每一个节点代表一条线段[a,b],长度为1的线段成为元线段,非元线段root都有两个子节点,左节点代表的线段为[a,(a+b)/2],编号为2*root;右节点代表的线段为[(a+b)/2+1,b],编号为2*root+1;
线段树的特点有四点如下:
(1):线段树是建立在线段之上,每一个节点代表一条线段[a,b];
(2):线段树是一种二叉树的结构,操作都是递归的;
(3):线段树是一种基于分治思想的数据结构,把问题实例划分成子实例,并分别递归的解决每一个实例,最后把子实例的解组合起来。
(4):把线段组织成树,在树中对线段进行操作。
线段树的储存结构一般都是用顺序储存结构,用数组保存树中的线段信息,一般结构如下:
struct node{
int left, right, mid;
}tree[MAX];
开的空间一般都是区间范围的3倍左右。
线段树主要适用对一个区间频繁的进行操作,然后各个区间的信息,对于不同的题目,增加不同的信息。
线段树主要有三种操作:
(1)建立线段树:
void build(int root,int l,int r)
{
tree[root].l = l;
tree[root].r = r;
tree[root].mid = (l+r)>>1;
if(l==r)
return;
build(2*root,l,tree[root].mid);//建立左子树
build(2*root+1,tree[root].mid+1,r);//建立右子树
}
(2)线段树的更新操作:
为了记录节点中的线段是否被完全覆盖过,我们需要在结点中添加cover数据域,若cover=1表示此线段已经被完全覆盖过了,否则未覆盖。
void update(int root,int l,int r)
{
if(tree[root].l == l && tree[root].r == r)
{
tree[root].cover = 3;
return;
}
if(r<=tree[root].mid)
update(l,r,2*root);
else if (l>tree[root].mid)
update(l,r,2*root+1);
else
{
update(l,tree[root].mid,2*root);
update(tree[root].mid+1,r,2*root+1);
}
}
(3)线段树的统计操作:
int ans=0;
void query(int root,int l,int r)
{
if(tree[root].l == l && tree[root].r == r)
ans += tree[root].cover,return;
if(l>tree[root].mid)
query(2*root,l,r);
else if(r<=tree[root].mid)
query(2*root+1,l,r);
else
{
query(2*root,l,tree[root].mid);
query(2*root+1,tree[root].mid+1,r);
}
}
线段树要去多练习,这样才会有收获!
北大上的线段树题目有:
POJ 1177,POJ 2182,POJ 2352,POJ 3264,POJ 3468,POJ 2777,POJ 2528,POJ 3667,POJ 1823,POJ 3368;