線段樹的入門級 總結
線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。
對於線段樹中的每乙個非葉子節點[a,b],它的左兒子表示的區間為[a,(a+b)/2],右兒子表示的區間為[(a+b)/2+1,b]。因此線段樹是平衡二叉樹,最後的子節點數目為n,即整個線段區間的長度。
使用線段樹可以快速的查詢某乙個節點在若干條線段中出現的次數,時間複雜度為o(logn)。而未優化的空間複雜度為2n,因此有時需要離散化讓空間壓縮。
【以下以 求區間最大值為例】
先看宣告:
[cpp]view plain
copy
#include
#include
const
intmaxnode = 2097152;
const
intmax = 1000003;
struct
nodenode[maxnode];
intfather[max];
// 每個點(當區間長度為0時,對應乙個點)對應的結構體陣列下標
【建立線段樹(初始化)】:
由於線段樹是用二叉樹結構儲存的,而且是近乎完全二叉樹的,所以在這裡我使用了陣列來代替鍊錶上圖中區間上面的紅色數字表示了結構體陣列中對應的下標。
在完全二叉樹中假如乙個結點的序號(陣列下標)為 i ,那麼 (二叉樹基本關係)
i 的父親為 i/2,
i 的另乙個兄弟為 i/2*2 或 i/2*2+1
i 的兩個孩子為 i*2 (左) i*2+1(右)
有了這樣的關係之後,我們便能很方便的寫出建立線段樹的**了。
[cpp]view plain
copy
void
buildtree(
inti,
intleft,
intright)
// 該結點往 左孩子的方向 繼續建立線段樹,線段的劃分是二分思想,如果寫過二分查詢的話這裡很容易接受
// 這裡將 區間[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[ ] 陣列儲存過 每單個結點 對應的下標了,因此我只需要知道第幾個點,就能知道這個點在結構體中的位置(即下標)了,這樣的話,根據之前已知的基本關係,就只需要直接一路更新上去即可。
[cpp]view plain
copy
void
updatatree(
intri)
【查詢區間最大值】:
將一段區間按照建立的線段樹從上往下一直拆開,直到存在有完全重合的區間停止。對照圖例建立的樹,假如查詢區間為 [2,5]
紅色的區間為完全重合的區間,因為在這個具體問題中我們只需要比較這 三個區間的值 找出 最大值 即可。
[cpp]view plain
copy
intmax = -1<<20;
void
query(
inti,
intl,
intr)
i = i <
if(l <= node[i].right)
i += 1; // right child of the tree
if(r >= node[i].left)
}
線段樹總結
線段樹總結 線段樹的原理就是每乙個區間都可以被分成若干個不相交連續區間 重要 線段樹維護的資料 1.自身結構的資料 比如 左兒子 右兒子的編號 2.懶惰標記 整段區間都變成乙個值 或者將要進行什麼操作 根據每次操作的型別 把操作的區間分成若干個不連續的區間 然後把操作的標記賦值給相應的區間 3.答案...
線段樹總結
解決的題目 對區間所對應的一些資料進行修改,查詢。基本步驟 先建樹,然後插入資料,然後更新,查詢。關鍵部分 用線段樹解題,關鍵是要想清楚每個節點要存哪些資訊以及這些資訊如何高效更新,維護,查詢。不要一更新就更新到葉子節點,那樣更新效率最壞就可能變成o n 的了。建樹的方式 1 陣列 若根節點下標為0...
線段樹總結
線段樹能解決哪些問題 下面給出線段樹的幾個應用 1 有一列數,初始值全部為0。每次可以進行以下三種操作中的一種 a.給指定區間的每個數加上乙個特定值 b.將指定區間的所有數置成乙個統一的值 c.詢問乙個區間上的最小值 最大值 所有數的和。給出一系列a.b.操作後,輸出c的結果。問題分析 這個是典型的...