眾所周知,線段樹是oi當中十分重要的乙個資料結構,所以我們今天就來講一講這個線段樹。
首先,我們來了解一下什麼是線段樹。
給定乙個1~ n的區間,我們考慮,將這個區間進行二分,使得這個區
間下擁有兩個小區間,如此反覆操作,直至這個區間被二分的只剩下乙個點,
那麼這就是這棵線段樹的葉節點,線段樹的實質上是一顆二叉樹,但未必是
一顆完全二叉樹。
那麼,引入這樣的線段樹結構有什麼用呢?
我們可以發現,對於線段樹上的乙個結點,其父節點的資訊一定是它和它兄
弟的綜合,也就是說,我們查詢乙個區間內的資訊,就只需要遍歷到乙個(或
多個的綜合)代表著該區間的結點,就可以得
到我們想要的資訊,而不需要遍
歷線段上的每個點,顯然這樣的效率是會大大優化的,通過理論證明,我們可
以發現,對於一棵線段樹,他的每次操作的時間複雜度是o(qlog2(n))的,而它的最大空間複雜度可以近似看為o(4n),事實上可以證明,實際的空間複雜度約為o(大於等於2n的最小的2的整數次方)。這樣的空間複雜度是較高的,
因此我們常常使用離散化的方式降低線段樹的空間複雜度。
接下來我們講一下如何進行程式設計實現。
首先,我們考慮如何進行定點修改,顯然對於一次修改,我們可以直接通
過從整條線段上一直進行二分,找到它所對應的結點,再進行修改,之後
回溯修改它父親結點的資訊即可,這樣的效率顯然是o(log2(n))的。(標程略)
接下來我們來考慮如何查詢區間內的資訊,對於乙個區間,顯然要麼它恰
好是線段樹上的乙個區間,要麼它就是由多個相鄰區間拼湊而成,因此我們
同樣考慮進行遞迴回溯,使得這個區間內的所有子區間被查詢到且不重複,
進行合併處理的出最後答案,事實上這樣的效率也是o(log2(n))的。(附簡單標程,pushdown會在後文中提到:))
1 query(int l,int r,int a,int b,int t)點此檢視查詢函式query
現在我們繼續進行拓展,要如何進行區間修改呢?
我們考慮如果僅僅是按照定點修改的方法進行處理,每次操作的複雜度
是o(nlog2(n)),這樣顯然是複雜度過高不可接受的。
考慮採用類似查詢的操作進行處理,這時出現了乙個問題,我們只能更
改到區間的值,如果這時我們查詢了這個區間的子區間的值,顯然這會導致
我們的解不是當前狀態下的解,
因此,我們考慮將區間更改的值進行延遲傳遞,這就是我們常常聽到的線段樹的lazy標記(我個人習慣稱之為mark)。
這裡的延遲傳遞是什麼概念呢?
我們可以這麼認為,在進行乙個操作時,只要我所遞迴到的區間的值是當前狀態即可,否則我就沒有更新的必要,因此,我們將每次要進行更新的值暫時存在乙個已經訪問到的區間,只有我遞迴到了它的子區間,我才需要進行值得更新傳遞,這也就是我們所說的lazy標記傳遞。(附上線段樹延遲標記傳遞的函式)
1點此檢視延遲標記的傳輸函式pushdownvoid pushdown(int t,int l,int r)
接下來,我們來看一下如何進行區間修改(附相關函式)
1 update(int a,int b,int l,int r,int t,int add_val)於是我們就講完了線段樹的一些基本操作了,撒花~~8pushdown(t,a,b);
9int m=(a+b)/2;10
if (m>=l) update(a,m,l,r,t*2
,add_val);
11if (m1,b,l,r,t*2+1
,add_val);
12 tree[t].val=tree[t*2].val+tree[t*2+1
].val;
13 }
資料結構 線段樹
啦啦啦啦啦啦線段樹是個好東西 好吧並沒有什麼好的 但貌似還是很好啊 線段樹就是一棵樹!顧名思義 又是這個詞 就是求關於一段的某些什麼什麼東西。比如區間最大值啊什麼的。引用百科知識 線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。對於線段樹中...
資料結構 線段樹
一 目標 1.如何快速的查詢出下列陣列arr 2,5 的和 2。以及更新arr 4 為6。用普通的方法查詢的複雜度為o n 更新的複雜度為o 1 這時候我們可以用線段樹來快速完成這些操作,複雜度為logn。二 內容 如何建立,查詢,更新線段樹。public class qurqpd int tree...
資料結構 線段樹
線段樹是一顆平衡的二叉搜尋樹,他以空間換區時間,讓線性查詢加速log級別的查詢,用到的演算法主要是二分搜尋和遞迴。例如 有陣列data 我有乙個需求,我需要頻繁的查詢區間i j的sum和。這裡先給出兩個解決方案 如果使用最普通的演算法遍歷,那麼查詢和更新的複雜度為o n 當然你還可以使用動態規劃,定...