關於Splay的學習感受

2022-08-22 02:12:13 字數 3102 閱讀 3867

之前記得五月份聽過一次外省金牌選手講過一次,然後七月份又講過一次,但本人腦子比較笨,當時完全聽得一臉懵逼啊,練了兩個月確實不一樣,現在談一下學習splay的一些感受。

首先欲知splay為何物,不得不先講講它的祖宗:二叉查詢樹,即bst(binary search tree),關於二叉查詢樹,它不是一顆空樹就是擁有以下幾個性質的二叉樹:

1.樹中每個結點被賦予了乙個權值;(下面假設不同結點的權值互不相同。)

2.若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;

3.若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;

4.左、右子樹也分別為二叉查詢樹;

下圖就是乙個合法的bst(蜜汁畫風【捂臉】):

通常二叉查詢樹支援如下操作:查詢乙個元素x在集合中是否存在;插入或刪除乙個元素,這些都可以通過遞迴實現。

splay,即伸展樹,是二叉查詢樹的一種改進,它與二叉查詢樹性質相似,與其不同的就是它可以自我調整。

關於splay操作:伸展操作 splay(x, s)是在保持伸展樹有序性的前提下,通過一系列旋轉將伸展樹 s 中的元素 x調整至樹的根部。在調整的過程中,要分以下三種情況分別處理:這個東西常數不能靠譜的可持久化,所以比起treap而言,splay的優勢似乎僅在於求lct。

1.節點x的父節點y是根節點:如果x是左兒子,那麼就右旋一下,如果是右兒子,那麼就左旋一下,一次操作後,x就變成根節點了,完成,下面是示意圖:

2.節點x的父節點y不是根節點,y的父節點為z,且x,y均為父節點的左兒子或右兒子,那麼就右旋或左旋兩次,分別交換y,z和x,y,下面是示意圖(靈魂畫師再次上線):

3.節點x的父節點y不是根節點,y的父節點為z,x,y乙個是左兒子,乙個是右兒子,這時如果x是左兒子就先右旋再左旋,如果是右兒子就先左旋再右旋,注意兩次都是轉x,示意圖:

所以由上圖可以感性理解一下,經旋轉的平衡樹確實比之前要平衡很多,注意每次操作後都要進行伸展操作,然後這樣下來各操作複雜度均攤為o(log n)(因為不會證所以我就直接跳了)。

splay支援以下操作:

1.find(x,s):判斷元素x是否在伸展樹s表示的有序集中。首先,訪問根節點,如果x比根節點權值小則訪問左兒子;如果x比根節點權值大則訪問右兒子;如果權值相等,則說明x在樹中;如果訪問到空節點,則x不在樹中。如果x在樹中,則再執行splay(x,s)調整伸展樹。

2.insert(x,s):將元素x插入伸展樹s表示的有序集中。首先,訪問根節點,如果x比根節點權值小則訪問左兒子;如果x比根節點權值大則訪問右兒子;如果訪問到空節點t,則把x插入該節點,然後執行splay(t,s)。

3.merge(s1,s2):將兩個伸展樹s1與s2合併成為乙個伸展樹。其中s1的所有元素都小於s2的所有元素。首先,我們找到伸展樹s1中最大的乙個元素x,再通過splay(x,s1)將x調整到伸展樹s1的根。然後再將s2作為x節點的右子樹。這樣,就得到了新的伸展樹s。如圖所示:

4.delete(x,s):把節點x從伸展樹表示的有序集中刪除。首先,執行splay(x,s)將x旋轉至根節點。然後取出x的左子樹s1和右子樹s2,執行merge(s1,s2)把兩棵子樹合併成s。如圖所示:

5.split(x,s):以x為界,將伸展樹s分離為兩棵伸展樹s1和s2,其中s1中所有元素都小於x,s2中的所有元素都大於x。首先執行find(x,s),將元素x調整為伸展樹的根節點,則x的左子樹就是s1,而右子樹為s2。圖示同上。

除了以上操作,splay還支援求最大值、最小值、前驅後繼,同時,splay還能進行區間操作,對於待操作區間[l,r],我們將l-1通過splay旋至根節點處,再將r+1旋至根節點右兒子處,這時r+1的左兒子就是待操作區間,可以像線段樹那樣打lazy-tag標記,然後具體實現見下方**(主要給rotate(左旋右旋合併版,只用打一段即可,不必打乙個zig打乙個zag),splay,build,lazy,find(int k)(尋找第k大數),getnext(求後繼,前驅類似)):

(因為從來不打指標所以只能夠打打陣列)

結構體:

struct num

tr[fa].fa=x;

push(fa);

push(x);

}

2.splay

void splay(int x,int goal)

if(goal==-1)

root=x;

push(x);

}

3.build

int build(int l,int r,int f)          //返回根節點

4.lazy

void lazy(int id)                  //此懶標記表示交換左右兒子,即區間反轉

}

5.find

int find(int k)

else

if(lsum+1==k)

break;

else

} return id;

}

6.getnext

int getnext(int id)

return p;

}

關於splay,在實現上還有許多細節,如每次操作後的splay,此處不再贅述。

以上大概是我關於splay的一些學習總結,以後可能還會填坑【ps:感謝wcr大佬的幫助】

splay 學習筆記

核心函式splay 每次訪問乙個節點,都把該節點轉到根 包括插入 查詢 操作 思想 對於訪問頻率較高的節點,使其處於根節點附近,從而保證log複雜度 splay可以維護中序遍歷是有序序列 也可維護 中序遍歷是當前操作後的序列 const int n 1e5 10 struct nodet n int...

splay學習報告

省選將至,慌得一批,hale趕緊去學了學splay,防止被打爆,雖說一定會被打爆的 不過這玩意是真的噁心,hale花了三個中午去搞掉他 一點點自己的理解啦,嚶嚶嚶 emmm,廢話少說進入正題 一 啥是平衡樹?平衡樹,即平衡二叉樹 balanced binary tree 具有以下性質 它是一棵空樹或...

Splay學習筆記

一種二叉樹的樹形資料結構,其定義如下 一種自平衡二叉搜尋樹,通過不斷將某個節點旋轉到根節點,使得整棵樹仍然滿足二叉查詢樹的性質,且保持平衡而不至於退化為鏈 root tot fa i child i 0 1 val i cnt i size i 根節點節點總數 父親左右兒子 點權出現次數 子樹大小 ...