原博地址https://laboo.top/2018/11/24/xds/#more算法
線段樹算法是一種快速查詢一段區間內的信息的算法, 因爲其實現簡單, 因此普遍應用於程序設計競賽中。
線段樹是一棵完美二叉樹, 即全部的葉子節點的深度均相同, 而且全部的非葉子節點都有兩個子節點。每一個節點維護一個區間, 這個區間爲父節點二分後的子區間, 根節點維護整個區間, 葉子節點維護單個元素, 當元素個數爲n
時, 對區間的操做均可以在O(log n)
的時間內完成, 由於此時樹的深度爲log2 n + 1
, 每次操做只需從葉子節點開始, 往上更新至根節點, 每層只需更新相關的一個區間便可, 操做次數log2 n + 1
, 即在O(log n)
的時間內可完成。數組
線段樹能夠提供不一樣的功能, 例如最多見的求區間內的最大最小值和求區間內的和, 還有其餘相似的功能, 實現思路基本相同spa
給定任意數列[a0, a1,...,an-1]
, 在O(log n)
的時間內完成下列的兩種操做設計
query(s, t)
求 [as,as+1,...,at-1]
內的最小值(最小值)update(i, x)
把 ai
的值改成 x
給定初始值全爲0
的數列[a0, a1,...,an-1]
, 在O(log n)
的時間內完成下列的兩種操做code
query(s, t)
求 [as,as+1,...,at-1]
內的和add(i, x)
執行 ai += x
這裏咱們以求區間最小值
內的最小值爲例, 用Python
來實現原始的一棵線段樹遞歸
這裏建立一個數組dat[]
並賦予初始最大值, 爲了讓其成爲一棵完美的二叉樹, 便於計算, 咱們把n
擴大到2的冪
, 因爲咱們在數組中填充了int32
的最大整數2147483647
, 因此多餘出來的的元素老是最大值, 不會影響原來區間的結果圖片
def init(self, n): self.INT_MAX = 2147483647 self.n = 1 while self.n < n: self.n *= 2 self.dat = [self.INT_MAX for i in range(2 * self.n - 1)]
咱們把一棵完美二叉樹壓成一個數組, 下標爲i
的子節點爲i*2+1 和 i*2+2
, a0
爲根節點, 每次更新時, 首先更新葉子節點, 以後一層層往上更新, 節點a[k] = min(a[k * 2 + 1],a[k * 2 + 2])
, 操做在O(log n)
的時間內完成get
def update(self, k, a): k += self.n - 1 self.dat[k] = a while k > 0: k = (k - 1) // 2 self.dat[k] = min(self.dat[k * 2 + 1],self.dat[k * 2 + 2])
query
的功能爲查詢[a, b)
區間內的最小值, 參數k, l, r
是輔助參數博客
當[a,b)
, 不在k
節點管理的區間[l, r)
內時, 直接返回INT_MAX
當[a,b)
, 重合於k
節點管理的區間[l, r)
時, 直接返回k
節點的值
不然, 遞歸k
的兩個子節點, 返回其中的最小值it
def query(self, a, b, k, l, r): if r <= a or b <= l: return self.INT_MAX if a <= l and r <= b: return self.dat[k] else: vl = self.query(a, b, k * 2 + 1, l, (l + r) // 2) vr = self.query(a, b, k * 2 + 2, (l + r) // 2, r) return min(vl, vr)
至此咱們就簡單地實現了一棵線段樹, 這只是線段樹的其中一種形式, 線段樹還有其餘的變體。線段樹的使用實例能夠看個人另外一篇文章https://laboo.top/2018/11/02/acm-lc-45/#more
歡迎關注個人博客公衆號