Go語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)開(kāi)點(diǎn)線(xiàn)段樹(shù)詳解
1、線(xiàn)段樹(shù)介紹
線(xiàn)段樹(shù)是一種用于高效處理區(qū)間查詢(xún)和區(qū)間更新的數(shù)據(jù)結(jié)構(gòu),當(dāng)我們需要解決一個(gè)頻繁更新區(qū)間值的問(wèn)題的時(shí)候,就可以采用線(xiàn)段樹(shù)的結(jié)構(gòu)進(jìn)行解決。線(xiàn)段樹(shù)的核心思想是將區(qū)間分為多個(gè)子區(qū)間進(jìn)行管理,越往下區(qū)間范圍越小,根節(jié)點(diǎn)表示整個(gè)線(xiàn)段樹(shù)能表示的區(qū)間。
本文記錄使用Go實(shí)現(xiàn)動(dòng)態(tài)開(kāi)點(diǎn)線(xiàn)段樹(shù)的方式,該模板的線(xiàn)段樹(shù)用于解決區(qū)間求和問(wèn)題,還有求解區(qū)間最小值、最大值的線(xiàn)段樹(shù)可以進(jìn)行微調(diào)修改即可。
區(qū)間查詢(xún)、區(qū)間更新的時(shí)間復(fù)雜度均為O(logN)。
2、動(dòng)態(tài)開(kāi)點(diǎn)線(xiàn)段樹(shù)實(shí)現(xiàn)
動(dòng)態(tài)開(kāi)點(diǎn)的核心在于,需要縮小范圍,即進(jìn)入子節(jié)點(diǎn)的時(shí)候再進(jìn)行創(chuàng)建,相對(duì)于使用數(shù)組來(lái)實(shí)現(xiàn)線(xiàn)段樹(shù),可以更大的減小空間開(kāi)銷(xiāo)。
線(xiàn)段樹(shù)節(jié)點(diǎn)
一個(gè)節(jié)點(diǎn)需要記錄它的左子節(jié)點(diǎn)、右子節(jié)點(diǎn)、當(dāng)前節(jié)點(diǎn)表示的區(qū)間的和val,以及暫未下推給子節(jié)點(diǎn)的懶惰值lazy。
type SegTreeNode struct { lazy int val int left *SegTreeNode right *SegTreeNode }
線(xiàn)段樹(shù)的創(chuàng)建
整個(gè)線(xiàn)段樹(shù)只需要記錄一個(gè)根節(jié)點(diǎn)以及該線(xiàn)段樹(shù)表示的區(qū)間上屆。
type SegTree struct { //線(xiàn)段樹(shù)的范圍,0~N N int root *SegTreeNode } // 創(chuàng)建線(xiàn)段樹(shù) func CreateSegTree(n int) *SegTree { return &SegTree{ N: n, root: &SegTreeNode{ lazy: 0, val: 0, left: nil, right: nil, }, } }
遞歸上推
當(dāng)更新完了子節(jié)點(diǎn)后,回到當(dāng)前節(jié)點(diǎn)的時(shí)候,需要更新當(dāng)前節(jié)點(diǎn)的值,表示從樹(shù)的底部上推值。
// 遞歸上推 func (ST *SegTree) Pushup(node *SegTreeNode) { node.val = node.left.val + node.right.val }
懶惰下推
當(dāng)需要縮小查找區(qū)間的時(shí)候,需要向下查找,這時(shí)候要先把懶惰值下推,防止查找出錯(cuò)誤的結(jié)果,也防止子節(jié)點(diǎn)還未創(chuàng)建。
// 同步下推 func (ST *SegTree) Pushdown(node *SegTreeNode, leftnum, rightnum int) { //創(chuàng)建左右節(jié)點(diǎn) if node.left == nil { node.left = new(SegTreeNode) } if node.right == nil { node.right = new(SegTreeNode) } //下推節(jié)點(diǎn)懶惰標(biāo)記 if node.lazy == 0 { return } node.left.val += leftnum * node.lazy node.right.val += rightnum * node.lazy //下推 node.left.lazy += node.lazy node.right.lazy += node.lazy //置零 node.lazy = 0 }
首先先創(chuàng)建左右節(jié)點(diǎn),如果沒(méi)有需要下推的懶惰標(biāo)記則直接返回。否則就更新左右節(jié)點(diǎn)的val和lazy。
更新操作
// 更新操作,更新[left,right]區(qū)間的值,start和end是當(dāng)前處在區(qū)間 func (ST *SegTree) Update(node *SegTreeNode, start, end, left, right, val int) { if left <= start && end <= right { //鎖定區(qū)間,進(jìn)行更新 node.val += (end - start + 1) * val node.lazy += val return } //縮小區(qū)間 mid := (start + end) / 2 //需要找到子節(jié)點(diǎn),先下推懶惰標(biāo)記 ST.Pushdown(node, mid-start+1, end-mid) if mid >= left { ST.Update(node.left, start, mid, left, right, val) } if mid+1 <= right { ST.Update(node.right, mid+1, end, left, right, val) } //遞歸 ST.Pushup(node) }
left和right表示要更新的區(qū)間,而start和end表示當(dāng)前區(qū)間。如果當(dāng)前區(qū)間處在需要更新的區(qū)間內(nèi),則直接更新區(qū)間值以及懶惰值,然后直接返回即可,此時(shí)不需要繼續(xù)更新下面節(jié)點(diǎn)的值,這是動(dòng)態(tài)開(kāi)點(diǎn)的關(guān)鍵所在。
若當(dāng)前區(qū)間并未完全處在需要更新的區(qū)間內(nèi),則二分該區(qū)間,縮小范圍進(jìn)行更新。
例如在一次操作需要更新的是[30,40]范圍的值,而當(dāng)前區(qū)間處在[25,50]中,當(dāng)前區(qū)間并未完全處在更新區(qū)間,則二分為[25,37]和[38,50],左區(qū)間和右區(qū)間均和需要更新的區(qū)間存在交集,那么就往下更新,直到更新區(qū)間包含當(dāng)前區(qū)間。
在更新完后,進(jìn)行一次上推。
查詢(xún)操作
與更新操作類(lèi)似,只需要一個(gè)ans來(lái)記錄答案并且返回。
// 查詢(xún)操作,返回區(qū)間的值 func (ST *SegTree) Query(node *SegTreeNode, start, end, left, right int) int { if left <= start && end <= right { return node.val } mid := (start + end) / 2 ST.Pushdown(node, mid-start+1, end-mid) ans := 0 if left <= mid { ans += ST.Query(node.left, start, mid, left, right) } if mid+1 <= right { ans += ST.Query(node.right, mid+1, end, left, right) } return ans }
到此這篇關(guān)于Go語(yǔ)言實(shí)現(xiàn)動(dòng)態(tài)開(kāi)點(diǎn)線(xiàn)段樹(shù)詳解的文章就介紹到這了,更多相關(guān)Go線(xiàn)段樹(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言中三個(gè)輸入函數(shù)(scanf,scan,scanln)的區(qū)別解析
本文詳細(xì)介紹了Go語(yǔ)言中三個(gè)輸入函數(shù)Scanf、Scan和Scanln的區(qū)別,包括用法、功能和輸入終止條件等,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-10-10Go簡(jiǎn)單實(shí)現(xiàn)協(xié)程方法
本文主要介紹了Go簡(jiǎn)單實(shí)現(xiàn)協(xié)程的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-12-12Go語(yǔ)言異常處理error、panic、recover的使用
GO語(yǔ)言中引入的異常的處理方式為error、panic、recover ,本文主要介紹了Go語(yǔ)言異常處理error、panic、recover的使用,感興趣的可以了解一下2024-08-08go語(yǔ)言編程實(shí)現(xiàn)遞歸函數(shù)示例詳解
這篇文章主要為大家介紹了go語(yǔ)言編程實(shí)現(xiàn)遞歸函數(shù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Golang Cron 定時(shí)任務(wù)的實(shí)現(xiàn)示例
這篇文章主要介紹了Golang Cron 定時(shí)任務(wù)的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05go json編譯原理XJSON實(shí)現(xiàn)四則運(yùn)算
這篇文章主要為大家介紹了go json編譯原理XJSON實(shí)現(xiàn)四則運(yùn)算示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07