[算法]线段树(IntervalTree)

转载请注明出处:http://www.cnblogs.com/StartoverX/p/4617963.html

  线段树是一颗二叉搜索树,线段树将一个区间划分成一些单元区间,每一个区间对应线段树的一个叶节点。对于线段树的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

[算法]线段树(IntervalTree)

线段树的特征:

  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)/;
  tree->right_child = buildTree(mid+,b);
  tree->left_child = buildTree(a,mid);
}
return tree;
}
//通常线段上会记录其他的信息,需要在建树时同时记录到树的每一个node。

线段树通常引入为了解决关于区间的问题,在树中插入线段,删除线段,查询线段的方法都和具体问题有关,我们以下面的问题为例: 

已知范围A1....An,在该范围内不断进行插入新线段(Ai...Aj,<=i<=j<=n),删除线段(Ai...Aj,<=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 = ;//初始时cover为0
tree->right_child = NULL;
tree->left_child = NULL;
if(b > a)
{
  int mid = (a + b)/;
  tree->right_child = buildTree(mid+,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)/;
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+,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)/;
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+,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)/;
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+,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

上一篇:UpdatePanel 控件,客户端事件生命周期踩坑


下一篇:浅析JavaScript正则表达式