[算法]线段树(IntervalTree)
转载请注明出处:http://www.cnblogs.com/StartoverX/p/4617963.html
线段树是一颗二叉搜索树,线段树将一个区间划分成一些单元区间,每一个区间对应线段树的一个叶节点。对于线段树的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
线段树的特征:
1.线段树的长度不超过logL(L是最长区间的长度)。
2.线段树把区间上的任意一条线段都分成不超过2logL条线段。
这些结论为线段树能够在O(logL)时间内完成一条线段的插入,删除,查找等工作,提供了理论依据。
线段树的用途:
线段树适用于和区间统计有关的问题。比如某些数据可以按区间进行划分,按区间动态进行修改,而且还需要按区间多次进行查询,那么使用线段树可以达到较快查询速度。
数据结构:
typedef struct IntervalTree
{
int right;
int left;
IntervalTree* right_child;
IntervalTree* left_child;
/*other information*/
//通常为了解题需要记录其他的信息。
}IntervalTree;
在线段上建树:
IntervalTree* buildTree(int a,int b)
{
IntervalTree* tree = new IntervalTree;
tree->right = b;
tree->left = a;
tree->right_child = NULL;
tree->left_child = NULL;
if(b > a)
{
int mid = (a + b)/2;
tree->right_child = buildTree(mid+1,b);
tree->left_child = buildTree(a,mid);
}
return tree;
}
//通常线段上会记录其他的信息,需要在建树时同时记录到树的每一个node。
线段树通常引入为了解决关于区间的问题,在树中插入线段,删除线段,查询线段的方法都和具体问题有关,我们以下面的问题为例:
已知范围A1....An,在该范围内不断进行插入新线段(Ai...Aj,1<=i<=j<=n),删除线段(Ai...Aj,1<=i<=j<=n)的操作,最后求某个线段被覆盖的次数?
为了解决这个问题,我们在线段A1...An上建树,并且在线段树中保存被覆盖的次数cover。
数据结构:
typedef struct IntervalTree
{
int right;
int left;
int cover;//保存被覆盖的次数。
IntervalTree* right_child;
IntervalTree* left_child;
}IntervalTree;
建树:
IntervalTree* buildTree(int a,int b)
{
IntervalTree* tree = new IntervalTree;
tree->right = b;
tree->left = a;
tree->cover = 0;//初始时cover为0
tree->right_child = NULL;
tree->left_child = NULL;
if(b > a)
{
int mid = (a + b)/2;
tree->right_child = buildTree(mid+1,b);
tree->left_child = buildTree(a,mid);
}
return tree;
}
插入新线段:
void insert(int a,int b,IntervalTree* tree)
{
if(tree->right == b && tree->left == a)
{
tree->cover++;
return;
}
int mid = (tree->left + tree->right)/2;
if(a > mid)
{
insert(a,b,tree->right_child);
}
else if(b <= mid)
{
insert(a,b,tree->left_child);
}
else
{
insert(a,mid,tree->left_child);
insert(mid+1,b,tree->right_child);
}
}
删除线段:
void delete_interval(int a,int b,IntervalTree* tree)
{
if(tree->right == b && tree->left == a)
{
tree->cover--;
return;
}
int mid = (tree->left + tree->right)/2;
if(a > mid)
{
delete_interval(a,b,tree->right_child);
}
else if(b <= mid)
{
delete_interval(a,b,tree->left_child);
}
else
{
delete_interval(a,mid,tree->left_child);
delete_interval(mid+1,b,tree->right_child);
}
}
查询线段覆盖次数:
int query(int a,int b,IntervalTree* tree)
{
if(tree->right == b && tree->left == a)
{
return tree->cover;
}
int mid = (tree->right + tree->left)/2;
if(a > mid)
{
return tree->cover + query(a,b,tree->right_child);
}
else if(b <= mid)
{
return tree->cover + query(a,b,tree->left_child);
}
else
{
int cover_right = query(mid+1,b,tree->right_child);
int cover_left = query(a,mid,tree->left_child);
return tree->cover + ((cover_right > cover_left) ? cover_right : cover_left);
}
}
线段树应用举例:POJ 3264:Balanced Lineup:http://www.cnblogs.com/StartoverX/p/4618041.html