**:學習下)
線段樹的入門級 總結
線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。
對於線段樹中的每乙個非葉子節點[a,b],它的左兒子表示的區間為[a,(a+b)/2],右兒子表示的區間為[(a+b)/2+1,b]。因此線段樹是平衡二叉樹,最後的子節點數目為n,即整個線段區間的長度。
使用線段樹可以快速的查詢某乙個節點在若干條線段中出現的次數,時間複雜度為o(logn)。而未優化的空間複雜度為2n,因此有時需要離散化讓空間壓縮。
【以下以 求區間最大值為例】
先看宣告:
#include #include const int maxnode = 2097152;
const int max = 1000003;
struct nodenode[maxnode];
int father[max]; // 每個點(當區間長度為0時,對應乙個點)對應的結構體陣列下標
【建立線段樹(初始化)】:
由於線段樹是用二叉樹結構儲存的,而且是近乎完全二叉樹的,所以在這裡我使用了陣列來代替鍊錶上圖中區間上面的紅色數字表示了結構體陣列中對應的下標。
在完全二叉樹中假如乙個結點的序號(陣列下標)為 i ,那麼 (二叉樹基本關係)
i 的父親為 i/2,
i 的另乙個兄弟為 i/2*2 或 i/2*2+1
i 的兩個孩子為 i*2 (左) i*2+1(右)
有了這樣的關係之後,我們便能很方便的寫出建立線段樹的**了
void buildtree(int i,int left,int right)
// 該結點往 左孩子的方向 繼續建立線段樹,線段的劃分是二分思想,如果寫過二分查詢的話這裡很容易接受
// 這裡將 區間[left,right] 一分為二了
buildtree(i<<1, left, (int)floor( (right+left) / 2.0));
// 該結點往 右孩子的方向 繼續建立線段樹
buildtree((i<<1) + 1, (int)floor( (right+left) / 2.0) + 1, right);
}
由於我事先用 father[ ] 陣列儲存過 每單個結點 對應的下標了,因此我只需要知道第幾個點,就能知道這個點在結構體中的位置(即下標)了,這樣的話,根據之前已知的基本關係,就只需要直接一路更新上去即可。
void updatatree(int ri)
【查詢區間最大值】:
將一段區間按照建立的線段樹從上往下一直拆開,直到存在有完全重合的區間停止。對照圖例建立的樹,假如查詢區間為 [2,5]
紅色的區間為完全重合的區間,因為在這個具體問題中我們只需要比較這 三個區間的值 找出 最大值 即可。
int max = -1<<20;
void query(int i,int l,int r)
i = i << 1; // get the left child of the tree node
if (l <= node[i].right)
i += 1; // right child of the tree
if (r >= node[i].left)
}
線段樹入門
線段樹 interval tree 是把區間逐次二分得到的一樹狀結構,它反映了包括歸併排序在內的很多分治演算法的問題求解方式。上圖是一棵典型的線段樹,它對區間 1,10 進行分割,直到單個點。這棵樹的特點 是 1.每一層都是區間 a,b 的乙個劃分,記 l b a 2.一共有log2l層 3.給定乙...
線段樹 入門
首先線段樹形象來說就是將陣列看成乙個線段,然後不斷的進行分割,儲存在樹中的不同節點上,有點類似於b 樹的定義吧 觀察上圖,首先將整個陣列的某種資訊 最大值或者最小值等 儲存在根節點,對應 1,8 然後對 1,8 線段進行平分,得到 1,4 和 5,8 兩個線段,掛在樹的第二層。這樣節點2儲存了陣列中...
線段樹入門
建議把那個文件開啟放邊上,然後看這個。涉及到 的部分本文可能會省略。not only success 線段樹 說到線段樹,首先要明確用陣列模擬樹的概念。以一顆二叉樹為例,若根為root 為1,為了方便陣列計算,那麼它的左右兒子可以用2,3來表示,即 root 1 和 root 1 1 類似於 在這個...