Splay樹分析


簡述

  Splay樹是一種二叉查找平衡樹,其又名伸展樹,緣由是對其進行任意操作,樹的內部結構都會發生類似伸張的動作,換言之,其讀和寫操作都會修改樹的結構。Splay樹擁有和其它二叉查找平衡樹一致的讀寫時間復雜度O(log2(n))。Splay樹的優點是實現簡單(苦於紅黑樹的小伙伴有福了),並且功能異常強大。其缺點其一是所有操作都會修改樹結構,因此對其進行的任意操作都需要進行同步,當然單線程就無需進行擔心。其二是Splay樹的時間復雜度的常數較大。

Splay實現

splay操作

  Splay樹的結點之間的關系與一般二叉樹相同,其任意結點左孩子的關鍵字不會超過當前結點,其右孩子的關鍵字不會小於當前結點,因此按中序遍歷得到的結點序列的關鍵字是遞增的。Splay樹的核心操作是splay(x),其將結點x通過旋轉移動到樹的頂端。一般二叉樹的旋轉大家估計都見過 ,但是splay的旋轉略有不同,其是通過雙旋使x的深度不斷降低,下面給出splay旋轉的規則(總共三個):

  1.若x的父親y是根結點。若x是y的左孩子,則進行下面的zig旋轉(若x是y的右孩子,則進行鏡像操作zag,即將所有左變為右,右變為左)

  

  2.若x的父親y不是根結點,記z為y的父親。若x是y的左結點且y是z的左孩子,則進行下面的zigzig操作(若對稱的x是y的右孩子且y是z的右孩子,則進行鏡像操作zagzag)。

  

  3.若x的父親y不是根結點,記z為y的父親。若x是y的右結點且y是z的左結點,則進行下面的zagzig操作(若對稱的x是y的左孩子,y是z的右孩子,則進行鏡像操作zigzag)。

  我們在splay的過程中不斷探測x此時的狀況,並選擇調用zig,zag,zigzig,zagzag,zigzag,zagzig六種操作中的一種,通過旋轉使得x不斷上升(x的深度降低),直到x成為樹根。

  實際上我們不需要完全為splay實現6種旋轉方案,只需要實現zig和zag即可。zigzag(x)與先后調用zig(x),zag(x)的結果一致,而zagzig則與先后調用zag(x),zig(x)的結果一致。但是要小心zigzig(x),其結果與調用兩次zig(x)是不同的,應該是先調用zig(x.f)后調用zig(x),這里x.f表示x的父親,而zazag也類似,等價於先調用zag(x.f),再調用zag(x)。這部分的說明請自行驗證。

  splay操作並不會影響對原樹和新樹進行中序遍歷得到的結果,即對於一對原樹中結點x,y,若x處於y的左子樹中,則在新樹中或者x處於y的左子樹中,或者y處於x的右子樹中。這都是來源於旋轉的直接性質。

插入

   下面說明插入insert(k)的具體流程,要插入關鍵字k,我們首先需要找到合適的插入位置,之后新建結點x並插入,之后對結點x進行splay操作。

連接

  連接join(x,y)用於將以x為根和以y為根的兩株splay樹連接為一株樹,其中x樹中所有結點的關鍵字都不大於y樹中的所有結點的關鍵字。如果x或y為空樹,則返回另外一株樹即可。否則在y中查找關鍵字最小的結點s,並對其調用splay操作。之后將x樹作為s的左孩子進行連接。

分裂

   split(k),將樹分裂為兩株子樹x與y,其中x中所有結點的關鍵字均小於k,而y中所有結點的關鍵字均大於等於k。我們先向樹中插入一個關鍵字為k的結點x(但是插入過程中我們需要保證若某個樹中結點的關鍵字為k,則x一定插入到該結點的左子樹中),之后對x進行splay手續。此時樹根為x,x的左孩子為小於k的樹,右孩子則為其余結點組合成的樹,移除x並返回其左右子樹。

刪除

  刪除操作delete(k),刪除關鍵字為k的任意一個結點。我們先找到某個關鍵字為k的結點x,若不存在,則對訪問到的最深的結點f執行splay手續。否則對x執行splay操作。之后我們移除x,並將其左右子樹作為兩株新樹,並利用連接操作進行連接。

查找

  find(k),查找關鍵字為k的任意一個結點。由於Splay樹的存儲是有序的,因此不斷地根據子樹根結點的關鍵字與k的關系,選擇繼續搜索其左子樹還是右子樹,或者根結點的關鍵字為k,這個流程與在一般二叉查找樹中尋找指定關鍵字的步驟和流程完全相同。如果找到,則返回該結點,否則返回空。不管是否找到,在離開前,都不要忘了為查找過的最深的頂點f執行splay手續。

時間復雜度

  很容易發現每次操作的時間復雜度與該次操作執行的splay操作的時間復雜度+O(1)是一致的,其中+O(1)是由於存在常數時間的費用。

  我們記第i次操作前數據結構的勢能為Di-1,而第i次操作后數據結構的勢能為Di,之后定義s(x)表示以x為根結點的子樹中結點總數,記d(x)=log2(s(x)),同樣定義s(T)為樹T中的結點總數。而我們將Splay樹的勢能定義為D=∑d(x),其中x取樹中的所有不同結點。記ci表示Splay操作中第i次上升x所付出的實際時間費用,我們認為每次操作的時間費用ci為1,即將其作為單位費用,記ci+Di-Di-1為第i次上升的攤還費用。很顯然D0=0,這也是勢能的下界,因此我們可以保證∑(ci+Di-Di-1)=∑ci+Dn-D0是該次操作時間復雜度的一個上界。

  對於一次對x的上升,其可能對應六種模式,zig,zag,zigzig,zagzag,zigzag,zagzig。不考慮鏡像模式(鏡像模式只是修改了左右,因此時間復雜度與原來的模式一致),我們需要分析zig,zigzig,zigzag的攤還費用。

  對於zig操作,觀察對應的圖,我們可以得出下面公式的成立(結點名稱后面加'表示變換后的結點):

$$ d\left(x'\right)+d\left(y'\right)-d\left(x\right)-d\left(y\right)=d\left(y'\right)-d\left(x\right)\le d\left(x'\right)-d\left(x\right)\le 3d\left(x'\right)-3d\left(x\right) $$

  而對於zigzig操作,觀察對應的圖,得出:

$$ d\left(x'\right)+d\left(y'\right)+d\left(z'\right)-d\left(x\right)-d\left(y\right)-d\left(z\right)=d\left(y'\right)+d\left(z'\right)-d\left(x\right)-d\left(y\right) $$

且由於

$$ 2d\left(x'\right)-d\left(z'\right)-d\left(x\right)=\log_2\left(\frac{\left[s\left(x'\right)\right]^2}{s\left(z'\right)s\left(x\right)}\right)\geqslant\log_2\left(4\right)=2 $$

從而得到

$$ d\left(y'\right)+d\left(z'\right)-d\left(x\right)-d\left(y\right)\le d\left(x'\right)+d\left(z'\right)-2d\left(x\right) $$ $$ \le d\left(x'\right)+d\left(z'\right)-2d\left(x\right)+2d\left(x'\right)-d\left(z'\right)-d\left(x\right)-2=3d\left(x'\right)-3d\left(x\right)-2 $$

  再考慮zigzag操作,觀察對應的圖,得出:

$$ d\left(x'\right)+d\left(y'\right)+d\left(z'\right)-d\left(x\right)-d\left(y\right)-d\left(z\right)=d\left(y'\right)+d\left(z'\right)-d\left(x\right)-d\left(y\right) $$

且同樣的有

$$ 2d\left(x'\right)-d\left(y'\right)-d\left(z'\right)\geqslant 2 $$

從而得到

$$ d\left(y'\right)+d\left(z'\right)-d\left(x\right)-d\left(y\right)\le d\left(y'\right)+d\left(z'\right)-2d\left(x\right) $$ $$ \le d\left(y'\right)+d\left(z'\right)-2d\left(x\right)+2d\left(x'\right)-d\left(y'\right)-d\left(z'\right)-2 $$ $$ =2d\left(x'\right)-2d\left(x\right)-2\le 3d\left(x'\right)-3d\left(x\right)-2 $$

  可以得出zig的攤還費用上界為3d(x')-3d(x)+1,而zigzig和zigzag的攤還費用上界為3d(x')-3d(x)。由此我們可以計算出一次splay操作的攤還時間復雜度上界為(我們將開始時的x記為x0,而第i次上升后的x記為xi,設t為總共上升次數):

$$ \sum_{i=1}^t{\left(3d\left(x_i\right)-3d\left(x_{i-1}\right)\right)}+O\left(1\right)+1=3d\left(T\right)-3d\left(x_0\right)+O\left(1\right)\le 3d\left(T\right)+O\left(1\right)=O\left(\log_2\left(|T|\right)\right) $$

因此我們到此已經證明了splay操作的攤還時間復雜度上界O(log2(|T|)),也間接地證明了所有splay操作的攤還時間復雜度上界均為O(log2(|T|))。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM