平衡樹——旋轉treap和WBLT

fhq_treap 和 Splay 之外的平衡樹的代碼是不可能寫的。僅供學術研究,所以只有口胡,沒有代碼。

  1. 旋轉treap

學過 fhq_treap 的都知道 treap 是啥吧(
但是 treap 也可以用旋轉來維護。

插入操作:
這是顯然的,找到位置插入以後,把結點一路轉上去,直到父親結點的 key 值小於當前結點的 key 值就行了。
容易發現這樣旋轉沒有破壞堆的性質。

刪除操作:
如果只有一個兒子那就把兒子轉上來,然後到子樹進行操作。兩個兒子轉較小的那個。
配幾張圖。圖中結點處標的都是 key 值。

好的我們要刪除 2 這個結點。4<5,把 4 旋轉上去。

這樣 2 就往下了一些。同理,5<8,把 5 轉上去。

只有一個兒子了。只能轉 8。

沒有兒子結點了。那就可以直接刪掉了。

剩下的操作和普通的平衡樹就沒什麼差別了,略。

  1. WBLT

2018年的國集論文裏有這個東西。還是非常 interesting 的。
WBLT 全名 Weight Balanced Leafy Tree。Weight Balanced Tree 是大家熟知的,但是這個 Leafy 就比較有趣了。
確切地說,Leafy 是對 BST 進行了一些爆改。
首先這棵樹的非葉子結點都有兩個兒子。原本的信息都存到葉子結點上,而非葉子結點存儲的信息是它的兩個兒子的合併。
具體到 WBLT 上,就是非葉子結點的權值和它的右兒子相同。(當然存 size 的時候還是要存子樹中有效結點的多少,即葉子結點的個數
但是 BST 的性質還是保留,也就是左子樹的葉子結點的值都小於右子樹的葉子結點的值。
畫個圖:

其中 1 2 3 4 就是存儲的信息。

接下來我們看這棵樹怎麼旋轉。
還是以上面的樹爲例,我們做一個左旋,把 4 轉上去的操作是這樣的:
image
記此時進行旋轉的根結點爲 x(這裏是 4''),左兒子記作 l(x), 右兒子記作 r(x)。
首先我們新建一個結點合併 l(x) 和 l(r(x))。
image
將 l(x) 設爲新建的結點。
image
將 r(x) 設爲 r(r(x))。
這樣就完成了左旋。原來的 r(x) 是不可能被遍歷到的,和已經刪除無異。(WBLT 中不儲存父親結點)
右旋同理。

然後我們就能用旋轉來維護樹的平衡了。如果左右子樹存儲的有效結點個數的比例失調,那麼我們就可以通過旋轉保持樹的平衡。
注:事實上雙旋的複雜度纔是正確的,但是單旋好像也不容易卡的樣子,不過我又不寫這個數據結構(

介紹插入刪除之前提一嘴,WBLT 的 find 操作和普通的平衡樹有一點不一樣,那就是要和當前結點的左兒子進行比較,這樣才能找到正確的結點。

插入刪除:
插入就是遞歸到葉子結點插進去就行了,然後建立兄弟結點,最後要像線段樹一樣 pushup 讓結點滿足 Leafy Tree 的性質。
以上面旋轉之後的樹爲例,我們要插入結點 6。最終結果如下圖:
image
事實上我們就是插入了 5,把 4 挪到 5 的兄弟的位置,然後 pushup 的時候把上面的值都改成了 5。
刪除也差不多。注意維護好 Leafy Tree 的性質就行。

剩下的操作略,注意結點存的 size 是子樹中葉子結點的多少。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章