特性
伸展树(Splay Tree)是特殊的二叉查找树,当某个节点被访问时,伸展树会通过旋转使该节点成为树根。
树结点和构造函数
public class SplayTree<T extends Comparable<T>> {
private class SplayTreeNode<T extends Comparable<T>> {
public T key;
public SplayTreeNode<T> left;
public SplayTreeNode<T> right;
public SplayTreeNode() {
this.left = null;
this.right = null;
}
public SplayTreeNode(T key, SplayTreeNode<T> left, SplayTreeNode<T> right) {
this.key = key;
this.left = left;
this.right = right;
}
}
// 根结点
private SplayTreeNode<T> mRoot;
}
复制代码
旋转
- 首选做一个循环,直到tree.left或者tree.right==null为止
- 当需要访问的key比每次循环的根结点的值小的时候,就需要右旋转;比根结点大的时候就需要左旋转。
- 左旋和右旋的方式和AVL树种LL和RR的方式一样,只是最后多一个赋值成新tree的操作。
- 链接的过程,如果是右旋的话(LL方式旋转)则设置一个N的空结点,让原来这颗树作为N的左孩子;如果是左旋的话(RR方式旋转)则设置一个N的空结点,让原来这棵树成为N的右孩子
private SplayTreeNode<T> splay(SplayTreeNode<T> tree, T key) {
if (tree == null)
return tree;
SplayTreeNode<T> N = new SplayTreeNode<>();
SplayTreeNode<T> l = N;
SplayTreeNode<T> r = N;
SplayTreeNode<T> c;
for (; ; ) {
int cmp = key.compareTo(tree.key);
if (cmp < 0) {
//在左子树上
if (tree.left == null)
break;
if (key.compareTo(tree.left.key) < 0) {
c = tree.left;
tree.left = c.right;
c.right = tree;
tree = c;
if (tree.left == null)
break;
}
//这里是link right
r.left = tree;
r = tree;
tree = tree.left;
} else if (cmp > 0) {
if (tree.right == null)
break;
if (key.compareTo(tree.right.key) > 0) {
//这里是一个类似AVL树RR旋转的过程
c = tree.right;
tree.right = c.left;
c.left = tree;
//这里就完成了一次旋转,形成新的tree
tree = c;
//判断tree是否有右结点,没有的话就旋转结束了
if (tree.right == null)
break;
}
//这里是一个link left的过程,在最上的结点上新增一个空结点,让这个空结点成为tree,
l.right = tree;
l = tree;
tree = tree.right;
} else
break;
}
//这里是组合的过程,这里不懂
l.right = tree.left;
r.left = tree.right;
tree.left = N.right;
tree.right = N.left;
return tree;
}
复制代码
插入结点
private SplayTreeNode<T> insert(SplayTreeNode<T> tree, SplayTreeNode<T> z) {
int cmp;
SplayTreeNode<T> y = null;
SplayTreeNode<T> x = tree;
while (x != null) {
y = x;
cmp = z.key.compareTo(x.key);
if (cmp < 0) {
//在左子树上;
x = x.left;
} else if (cmp > 0) {
//在右子树上
x = x.right;
} else {
z = null;
return tree;
}
}
if (y== null)
tree = z;
else {
cmp = z.key.compareTo(y.key);
if (cmp < 0)
y.left = z;
else
y.right = z;
}
return tree;
}
复制代码
删除结点
/*
* 删除结点(z),并返回被删除的结点
*
* 参数说明:
* bst 伸展树
* z 删除的结点
*/
private SplayTreeNode<T> remove(SplayTreeNode<T> tree, T key) {
SplayTreeNode<T> x;
if (tree == null)
return null;
// 查找键值为key的节点,找不到的话直接返回。
if (search(tree, key) == null)
return tree;
// 将key对应的节点旋转为根节点。
tree = splay(tree, key);
if (tree.left != null) {
// 将"tree的前驱节点"旋转为根节点
x = splay(tree.left, key);
// 移除tree节点
x.right = tree.right;
} else
x = tree.right;
tree = null;
return x;
}
public void remove(T key) {
mRoot = remove(mRoot, key);
}
复制代码