學習數據挖掘 1 : 線段樹

學習數據挖掘 1 : 線段樹

來自專欄學習數據挖掘6 人贊了文章

問題引入:售票系統問題

假設在一個火車線路上有五個車站,它們分別在A、B、C、D、E五座城市(因此區間數 N=4)。售票部門出售所能的車票,即起始站和終點站是任意的。由於城市之間距離較長,售票部分希望每位乘客都有座位,設總座位數是 M 。在處理新的訂單請求時,機智的程序員怎麼最快地判斷是否還有餘座呢?

拍腦袋給出的演算法

開一個 N 維的數組,分別存儲A-B, B-C, C-D, D-E段的乘客數,每當新訂單出現時,就更新相應的區間,只要 N 個數都小於乘客數 M (等價地, N 個數中最大數小於乘客數 M ), 就安排乘客上這趟車,否則告訴乘客余票不足。

每新添加一個訂單,需要重新找 N 個數中的最大數,因此計算複雜度是 O(N) 。這是最好的演算法嗎?

問題的分析

  • 售票系統是一個在線的系統,會有源源不斷的新數據(新訂單)湧入,因此保留歷史的中間變數可能會加速演算法。
  • 車站具有順序性。其次,一個顧客的乘坐區間只能是連續的,即他不可能在A站上火車,B站下火車,又在D站上同一趟火車,最後在E站下了車。

線段樹長神馬樣子?

仍然從售票系統例子出發。一開始售票數為0時,線段樹長這樣(圖1):

基本特點容易看出:這是一個二叉樹,在每個節點處,一個區間被分成了左、右兩個區間。在每個節點和葉子處,都有一個伴隨數字,這個數字代表這個區間的乘車人數(這麼說不嚴謹,具體怎麼更新,請大家耐著性子再往後看2333)。

首先打北邊來了個小饒同學,他的乘車區間是B-C, B-C就是葉節點,線段樹更新為(圖2):

然後打南邊來了個小鳴同學,他的乘車區間是B-D, 注意B-D=B-C+C-D, 而B-C和C-D是葉節點,線段樹更新為(圖3):

一直到現在都十分正常,和拍腦袋給出的演算法一樣。

這時急匆匆來了個小迪同學,他的乘車區間是A-D, A-D=A-C+C-D, C-D是葉節點這個沒問題,但A-C是一個中間節點,現在有兩種看法:

  • 繼續往下拆,A-C=A-B+B-C, 更新A-B 和B-C區間
  • 保持整體, 更新A-C區間,線段樹的哲♂學要求我們選擇這種更新策略,如圖4所示。

最後來了個小承同學,他買了張全程票,即A-E, 注意到A-E恰好就是根,於是不必往下拆了,更新A-E即可,如圖5所示。按照拍腦袋給出的演算法,等價的結果應該如圖5 所示。

是騾子是馬,拉出來溜溜了。我們需要知道區間的最多人數,從葉節點出發,我們可以算出所有區間人數的最大值(因為有 max(a,b,c,d)=max(max(a,b),max(c,d)))。如下圖所示:

兩種辦法求出的最大值都是4,可以說明線段樹和拍腦袋方法的等價性。好了,線段樹學習結束了。exm???那線段樹優勢在哪呢?


為什麼線段樹?

更新(添加新訂單)

  • 對於拍腦袋的辦法,需要 O(N) 的運算量
  • 對於線段樹,只需要 O(logN) 的運算量,嚴格的上界是 2logN
    • 簡單地理解,當一個區間恰好等於一個節點(而不必是葉節點),更新即可完成

查詢(計算所有區間的最多乘客數)

  • 對於拍腦袋的辦法,採用打擂法求最大值,需要 O(N) 的運算量
  • 對於線段樹,利用了樹的結構,還是需要 O(N) 的計算量,而且在中間節點處要記得加上相應的數(專業名稱:lazy tag等等)。

所以線段樹的優勢,主要還是體現在在線(online)的問題上。


一般的線段樹

博客:這裡有嚴格定義和相應的實現代碼

【數據結構系列】線段樹(Segment Tree)?

www.cnblogs.com圖標

維基:這裡是一般的線段樹定義

Segment tree - Wikipedia?

en.wikipedia.org圖標
推薦閱讀:

數據挖掘過程中:數據預處理
利用Python繪圖
玩轉Pandas,讓數據處理更easy系列7
探索大數據挖掘技術在商業銀行領域的應用
數據分析思維-提供另外一種審視世界的視角

TAG:數據挖掘 | 數據結構 |