標籤:
轉載請註明出處: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
[演算法]線段樹(IntervalTree)