转载请注明出处: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
时间: 2024-10-29 16:48:36