标签:
伸展树(英语:Splay Tree)是一种二叉查找树,它能在O(log n)内完成插入、查找和删除操作。它是由丹尼尔·斯立特(Daniel Sleator)和罗伯特·塔扬在1985年发明的[1]。
在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法, 在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。伸展树应运而生。伸展树是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。
它的优势在于不需要记录用于平衡树的冗余信息。
——来自维基百科
几个常用操作:
其实也不一定对。。。
rotate操作
void rotate(int x,int &k) { int y = f[x], z = f[y], l, r; if (tr[y][0] == x) l = 0; else l = 1; r = l ^ 1; if (y == k) k = x; else { if (tr[z][0] == y) tr[z][0] = x; else tr[z][1] = x; } f[x] = z; f[y] = x; f[tr[x][r]] = y; tr[y][l] = tr[x][r]; tr[x][r] = y; }
把x向上旋转一个
r = l ^ 1; 01取反
splay操作
void splay(int x,int &k) { int y,z; while (x != k) { y = f[x]; z = f[y]; if (y != k) { if ((tr[z][0] == y)^(tr[y][0] == x)) rotate(x,k); else rotate(y,k); } rotate(x,k); } }
把x转到k的位置
y !=k 至少要旋转两次,否则直接旋转一次x就行了
(tr[z][0] == y)^(tr[y][0] == x) 判断在不在一条直线上
如果是的话,先旋转父节点,再旋转自己;否则先旋转自己,再旋转父节点(就是旋转两次自己)
因为调用了rotate,在rotate中k的值改变了,所以在这里也要加&
插入元素
void ins(int &k,int x,int last) { if (k == 0) { size ++; k = size; num[k] = x; f[k] = last; splay(k,rt); return; } if (x < num[k]) ins(tr[k][0],x,k); else ins(tr[k][1],x,k); }
插入一个元素
操作完成后k的值会变成size,size会转到根,所以k的值要返回给rt,所以要加上&
删除元素
void del(int x) { splay(x,rt); if (tr[x][0] * tr[x][1] == 0) rt = tr[x][0] + tr[x][1]; else { int k = tr[x][1]; while (tr[k][0]) k = tr[k][0]; tr[k][0] = tr[x][0]; f[tr[x][0]] = k; rt = tr[x][1]; } f[rt] = 0; }
删除编号为x的结点
先旋转到根,如果只有一棵子树,直接删除,让子树的根作为根节点
否则寻找右子树中最小的那个点,作为根节点
寻找前驱
void ask_before(int k,int x) { if (k == 0) return; if (num[k] <= x) { t1 = k; ask_before(tr[k][1],x); } else ask_before(tr[k][0],x); }
寻找后继
void ask_after(int k,int x) { if (k == 0) return; if (num[k] >= x) { t2 = k; ask_after(tr[k][0],x); } else ask_after(tr[k][1],x); }
标签:
原文地址:http://www.cnblogs.com/liumengyue/p/5218473.html