1. 程式人生 > 實用技巧 >SPLAY 未完待咕

SPLAY 未完待咕

Splay(還沒完先咕著)

變數定義:
\(n:節點個數~~~ch[x][2]~0代表x左兒子~1代表右兒子\)

\(val[x]x儲存的值~~cnt[x]代表x儲存的重複權個數~~fa[x]x爸爸~~siz[x]x子樹下權值數\)

操作:

chk,查詢節點父親方向

pushup,更新size陣列值

void pushup(int x){siz[x] = siz[ch[x][0]]+siz[ch[x][1]]+cnt[x]}
旋轉(rotate)

最開始樹是這樣的

我們把2號點整到4號點位置,2的下面就有子樹1,3,4,5

一種優秀做法是把4->2改成4->3,6->4改成6->2,2->3改成2->4

splay需要rotate保持平衡,核心操作,旋轉後中序遍歷和合法性保持不變

void rotate(int x){
	int y = fa[x],z = fa[y],k = chk(x),w = ch[x][k^1];
	ch[y][k] = w,fa[w] = y;
	ch[z][chk(y)] = x;fa[x] = z;
	ch[x][k^1] = y;fa[y] = x;
	pushup(y);pushup(x);
}
伸展(splay)

將一個節點一路rotate到指定節點的兒子。如果該點,fa,grandfa節點三點一線,應該先轉fa

void splay(int x,int goal = 0){
	while(fa[x] != goal){
	int y =fa[x],z = fa[y];
	if(z != goal){
	if(chk(x) == chk(y)) rotate(y);
	else rotate(x);
	}
	rotate(x);
  }
  if(!goal) root = x;
}
find操作
void find(int x){
	if(!root) return;
	int cur = root;
	while(ch[cur][x > val[cur]] && x != val[cur]){
		cur = ch[cur][x > val[cur]];
	}
	splay(cur);
}
insert插入

從根節點開始,一路搜尋下去。如果節點存在則直接自增cnt的值。否則新建節點並與父節點連邊。因為新建節點時可能會拉出一條鏈,所以新建節點後需要將該節點splay到根節點。沿途的rotate操作可以使平衡樹恢復平衡。

void insert(int x){
	int cur = root,p = 0;
    while(cur && val[vur] != x){
    
    }
}