特性
伸展樹(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);
}
複製程式碼