手寫線段樹 第一場第一鏡

2021-09-24 14:41:52 字數 3033 閱讀 2306

靜謐的夜最適合刷演算法題了。刷著刷著發現了乙個好玩的資料結構叫做線段樹,據說是演算法競賽的常客哦,於是就自己寫乙個簡單的玩玩。

我用它來主要是為了快速找到陣列某區間內的數字和,並且在修改陣列某幾個元素之後再次找區間內的數字和。可想而知,我有兩個需求:求和,修改。

正常情況下,因為我們並不想給陣列排序,那麼,我們可以用o(1)的複雜度進行修改,用o(n)的複雜度遍歷區間來實現求和。但是,當我們需要頻繁進行求和的操作時,看起來很美的o(n)就變成了牆上的蚊子血,再也不是當初的紅玫瑰了,於是,我們只能優化它。

我選擇了線段樹,典型的空間換時間。我用陣列實現了二叉的線段樹,用了4倍的額外空間。這顆二叉樹的葉子節點是原始陣列的各個元素,非葉子節點儲存的為某段區間的和,一直到根節點逐步合併區間,根節點正好就是原始陣列從頭到尾的最大區間的元素和,具體的可以看看**,個人覺得**比文字更直觀哈哈。這樣的處理將求和操作變成了o(logn),而相應的修改操作也增加到了o(logn),兩個對數級別往往總是要好過乙個常數的和乙個線性的,不是麼?

talk is cheap. show me the code.

首先是構建線段樹,明白原理之後就像二叉樹一樣直接遞迴著搞就好啦:

type segmenttree struct

//初始化線段樹,至於為什麼需要4倍空間,只要咱們理解了二叉樹就一目了然了,線段樹每個節點儲存的就是某一段的區間和

func

newsegmenttree

(num [

]int

)*segmenttree

tree :=

make([

]int,4

*countnum)

if countnum >

0 leftchild :=

leftchild

(index)

rightchild :=

rightchild

(index)

mid := left +

((right - left)

>>1)

buildtree

(leftchild, left, mid)

buildtree

(rightchild, mid+

1, right)

tree[index]

= tree[leftchild]

+ tree[rightchild]

}buildtree(0

,0, countnum-1)

}return

&segmenttree

}

然後就是求和操作了,我們把各個區間的和分別儲存好了,本質上就變成了二叉樹上找常數個節點,所以當然是o(logn)了:

//求和操作,只需要通過遞迴來找到最近的區間和就好

func

(st *segmenttree)

sumrange

(start, end int

)int

leftchild :=

leftchild

(index)

rightchild :=

rightchild

(index)

mid := left +

((right - left)

>>1)

if start >= mid+

1else

if end <= mid

return

sum(leftchild, left, mid, start, mid)

+sum

(rightchild, mid+

1, right, mid+

1, end)

}return

sum(0,

0,len(st.data)-1

, start, end)

}

修改操作,好記性不如爛筆頭,用筆畫畫就可以:

//修改操作,遞迴找到葉子節點改掉索引對應的值,然後回溯的過程改掉包含索引的所有區間的和

func

(st *segmenttree)

update

(i int

, value int

) st.data[i]

= value

var up func

(int

,int

,int

) up =

func

(index, left, right int

) leftchild :=

leftchild

(index)

rightchild :=

rightchild

(index)

mid := left +

((right - left)

>>1)

if i >= mid+

1else

if i <= mid

st.tree[index]

= st.tree[leftchild]

+ st.tree[rightchild]}up

(0,0

, countnum-1)

}func

leftchild

(i int

)int

func

rightchild

(i int

)int

新手的**總是有很大的優化空間的,走過路過的大爺們要不吝賜教哦。

當然了,這僅僅只是乙個簡單的線段樹,只能解決我的小需求。還有更多的方案,比如用鍊錶來構建樹肯定要更靈活,而且線段樹並不僅僅用來求和,可以實現更多面向區間的操作,更好玩的是可以懶惰更新,每次修改不著急重新構建整棵樹,可以一點一點地來,這樣更加優化了效能。大家如果對線段樹感興趣的話,查資料去吧~o( ̄︶ ̄)o

演算法夢想家,來跟我一起玩演算法,玩**,聊聊文學創作,咱們一起天馬行空!

第一場雪 杭州

上周五杭州還熱得要命,週六就開始下雨凍人。前兩天楓葉還火紅得溢著秋意,今天就雪壓青枝了。這是我見過的第一場雪,也是今年杭州的第一場雪。以前去北方都是夏天,所以導致都沒見過雪。想起以前的同桌邀請我過去哈爾濱觀賞冰雕,我心裡有點惴惴,怕自己去了成了被觀賞的就慘了。發幾張 早上起來的時候是很小的顆粒狀。走...

暑期第一場題解

此題,emmmm,有毒,很容易出現各種bug,還是自己菜,不能動不動就覺得是資料有問題,此題肯定要用字串,模擬的題,有點長 蒟蒻瑟瑟發抖 不過不難理解,仔細看看就明白了,主要是細節方面的處理 include include include includeusing namespace std int...

暑期集訓第一場

找規律,當n 3 0的時候,ans pow n 3,3 當n 4 0的時候,就是pow n 2,2 n 4。include include define ll long long using namespace std ll t,n intmain else if n 4 0 else 題目中說了不...