當我覺得我學習演算法剛剛從萌新到入門的時候,一類給定乙個區間然後給定一系列操作的題徹底的打擊了我,那時我才醒悟,程式設計路上,我一直是萌新。
線段樹是乙個具有樹特性的資料結構,它是一顆二叉搜尋樹。如下圖為區間[1,10]所建立的線段樹
將每乙個區間序列二分成小區間,線段樹就儲存小區間的資訊,也就是每個小區間對應線段樹中的乙個結點。比如上圖根節點對應[1,10]
由於線段樹的二分性質,對每乙個子節點來說,左兒子的區間小於右兒子的區間,所以線段樹也是平衡樹。
將需要處理的資訊看成乙個個點,也就是葉子節點,然後通過父親節點來將資訊整合,做到通過樹結構來進行操作對節點資訊進行增刪查改,大大降低複雜度。
最簡單的應用就是記錄區間是否被覆蓋,隨時查詢當前被覆蓋區間的總長度。
既然線段樹利用區間二分建樹,那麼對子區間進行操作,只需要從根節點通過遞迴找到此區間即可,
一次操作時間肯定與樹的高度有關,由於二叉樹搜尋樹的關係,它的高度為log2(n),
所以完成一次操作的時間為o(log(n))。
那麼回想前言中的題目:
給定區間n個數,n<=1000000,給定m個操作,m<=10000,對於每個操作,有兩種情況
用列舉法肯定超時,然而有了線段樹,就可以用log2(n)的時間完成每次操作,資料量很大時,不怕超時。
更多操作,接下來會更細緻的講解。
建一顆樹,當然可以用遞迴和結構體陣列。(還有非遞迴版本,暫時不給出)
int n;線段樹是完全二叉樹,乙個序號為k的節點,它的左兒子序號為2*k,右兒子序號為2*k+1。、struct
nodenode[
4*n];//注意要開四倍空間
即 k ---> k << 1
---> k << 1 | 1
對於乙個[ l,r ]區間,結構體中的left,表示此節點代表此區間的左端點l,right表示此區間的右端點r,sum表示此區間的總資訊(這裡是元素之和)
於是可以用遞迴來實現。
**:
void pushup(int n)
void build(int l,int r,int具體實現看下圖:k)
int m=(l+r)/2
; build(l,m,
2*k);
build(m+1,r,2*k+1
);pushup(k); //這裡表示回溯函式
}
圖中紅色箭頭表示build遞迴創造節點,紫色箭頭表示pushup回溯整合資訊,
節點上面表示創造的序號,
節點下面的數字表示節點代表的區間的總和。
這個問題很重要,不然隨意開空間可能會導致溢位。
假設一顆線段樹最下面一層最多有n個節點,是乙個滿二叉樹,那麼此線段樹則有log2(n)+1層。
但如果像[1,10]這樣,不是滿二叉樹,那麼(取整) log2(n) < log2(n)+1 ,所以這樣的線段樹最多有log2(n)+2層。
而二叉樹節點的個數為2log2(n)+2=4*n,所以要開4倍空間。
根據上面所述:當某個節點的left==right時,它是乙個葉子節點,也就是我們需要查詢和修改的物件,於是可以通過遞迴線段樹來找到需要的節點資訊。
理解線段樹
線段樹 二叉搜尋樹 與區間樹 相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。使用線段樹可以快速的查詢某乙個節點在若干條線段中出現的次數,時間複雜度為o logn 對於線段樹中的每乙個非 葉子節點 a,b 它的左兒子表示的區間為 a,a b 2 右兒子表示的區間為 a b...
線段樹初步理解
原本這兩天在搞網路流的.但最近幾次網賽多次遇到了線段樹的題目.意識到線段樹還是重要的.所以今天就初步的了解了線段樹最簡單的應用.單點更新.今天學習線段樹主要是看notonlysuccess大牛的模板 風格非常好.很適合初學線段樹的來參考.網上的一些ppt也不錯.一步一步動態描述線段樹的演算法過程.線...
理解 線段樹 掃瞄線
題目大意 給你n個矩形,求他們的總面積之和。解題思路 哈哈,不過大家別急,為了方便描述,自己動手畫了幾個。四條紅線為矩形的上下底邊,這裡我們稱之為掃瞄線 實際程式設計中不存在,只是乙個概念 如圖所示,要求兩個矩形的面積並,可以把矩形分成幾個小矩形,最後的面積總和為它們的和。對於每個小矩形其 面積s ...