Splay學習筆記

2022-07-19 03:30:12 字數 3893 閱讀 1710

一種二叉樹的樹形資料結構,其定義如下:

一種自平衡二叉搜尋樹,通過不斷將某個節點旋轉到根節點,使得整棵樹仍然滿足二叉查詢樹的性質,且保持平衡而不至於退化為鏈

\(root\)

\(tot\)

\(fa[i]\)

\(child[i][0/1]\)

\(val[i]\)

\(cnt[i]\)

\(size[i]\)

根節點節點總數

父親左右兒子

點權出現次數

子樹大小

void clear(int x)

void update(int x)

int get(int x)

為調換\(splay\)中父子節點的位置,我們使用旋轉操作,將乙個節點向上移動乙個位置,並保證:

\(splay\)中的旋轉分為兩種,左旋和右旋,這裡以右旋為例:

旋轉分為四個步驟\(:\)

(假設需要旋轉的節點為\(x\),其父親為\(y\))

\(1.\)將\(y\)的左兒子指向\(x\)的右兒子,且\(x\)的右兒子的父親指向\(y\)

child[y][0] = child[x][1];

fa[child[x][1]] = y;

\(2.\)將\(x\)的右兒子指向\(y\),且\(y\)的父親指向\(x\)

child[x][1] = y;

fa[y] = x;

\(3.\)如果\(y\)還有父親\(z\),將\(y\)原來在\(z\)中所在的位置指向\(x\),且\(x\)的父親指向\(y\)

fa[x] = z;

child[z][y==child[z][1]] = x;

\(4.\)更新\(x\)和\(y\)的\(size\)

void rotate(int x)
\(splay\)規定,每訪問乙個節點後,都要強制將該節點旋轉到根的位置

為保證不退化成鏈,\(splay\)操作一共分三步

\(1.\)如果父節點為目標位置,則向上旋轉

\(2.\)如果當前節點與父節點的「關係」和父節點與祖父節點的「關係」相同,則先旋轉父節點,再旋**身

\(3.\)如果不滿足以上條件,則將自身連續旋轉兩次

重複以上操作,直到旋轉到根

void splay(int x,int goal)

if (!goal) root=x;

}

設插入的值為\(k\)

if(!root)
同時需要將該節點\(splay\)到根的位置

int now = root,f = 0;

while(1)

f = now;

now = child[now][val[now]設\(x\)為需要查詢的值

根據二叉搜尋樹的性質:

int rank(int k)

else

now = child[now][1];}}

}

乙個數的前驅定義為小於\(x\)的最大的數

後繼定義為大於\(x\)的最小的數

顯然,乙個數的前驅是其左子樹中最靠右的節點,後繼是其右子樹的最靠左的節點

將\(x\)旋轉到根後查詢即可

int pre()

int next()

首先將要刪除的節點\(x\)旋轉到根的位置

否則

void del(int k)

if(!child[root][0]&&!child[root][1])

if(!child[root][0])

if(!child[root][1])

int now = root,x = pre();

fa[child[now][1]] = x;

child[x][1] = child[now][1];

clear(now);

update(root);

}

#includeusing namespace std;

const int maxn = 100005;

int child[maxn][2],size[maxn],root,tot,fa[maxn],val[maxn],cnt[maxn];

#define ci(q) scanf("%d",&q)

struct splay

bool pd(int x)

void clear(int x)

void rotate(int x)

void splay(int x)

root = x;

} void insert(int k)

int now = root,f = 0;

while(1)

f = now;

now = child[now][val[now]1)

if(!child[root][0]&&!child[root][1])

if(!child[root][0])

if(!child[root][1])

int now = root,x = pre();

fa[child[now][1]] = x;

child[x][1] = child[now][1];

clear(now);

update(root);

}}tree;

int main()

return 0;

}

給乙個長度為\(n\)的序列,序列中第\(a_i\)項的初始值為\(i\)

有\(m\)次區間翻轉操作,輸出經過 \(m\) 次變換後的結果

按點的編號建立一顆\(splay\)

每次翻轉時

先將\(val = l-1\)和\(val = r+1\)的節點分別轉到根和根的兒子節點

根據\(splay\)的性質,整顆樹的中序遍歷不變

因此只需要將\(child[child[root][1]][0]\)下的所有子樹交換

通過給打懶標記的方式來實現交換操作即可

#includeusing namespace std;

const int maxn = 100005;

const int inf = 114514191;

int child[maxn][2],a[maxn],size[maxn],root,tot,fa[maxn],val[maxn],cnt[maxn],tag[maxn];

struct splay

void update(int x)

bool pd(int x)

void clear(int x)

void rotate(int x)

int build(int l,int r,int f)

void splay(int x,int goal)

if (!goal) root=x;

} int kth(int k)

else

k-=t;

now = child[now][1];

}} }

void reverse(int x,int y)

void dfs(int x)

tree.dfs(root);

}

splay-oi-wiki

splay 學習筆記(一)-menci

splay 學習筆記

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

Splay學習筆記

n n 500000 個數,要求維護區間加,區間查詢 很簡單,用線段樹 樹狀陣列隨便寫寫就能過 n n 500000 個數,維護插入 刪除 找區間第k大 小 用平衡樹 set也能過 n n 500000 個數,維護區間翻轉,區間查詢,插入,刪除 這個時候就需要用到伸展樹 splay splay是一種...

學習筆記 splay入門(更新中)

宣告 本部落格所有隨筆都參照了網路資料或其他部落格,僅為博主想加深理解而寫,如有疑問歡迎與博主討論 終於學習了 spaly splay 聽說了很久,因為dalao總是那這個開玩笑所以對它有深深的恐懼.但是學起來沒有那麼難啦,可能是因為提前學了替罪羊樹?學替罪羊樹真的是痛苦555 p3369 模板 普...