每一次对节点的访问都会使从根到该节点的路径上的所有节点,从上向下开始算起,依次成为树的根部
//自顶向下伸展树
class SplayTree<T extends Comparable<? super T>>
{
//创建节点类
private static class BinaryNode<T>
{
T element;
BinaryNode<T> left;
BinaryNode<T> right;
BinaryNode(T element)
{
this(element, null, null);
}
BinaryNode(T element, BinaryNode<T> left, BinaryNode<T> right)
{
this.element = element;
this.left = left;
this.right = right;
}
}
//声明根节点
private BinaryNode<T> root;
//声明一个空节点
private BinaryNode<T> nullNode;
//为了伸展定义一个头,左子树是左树,右子树是右树
private BinaryNode<T> header = new BinaryNode<T>(null);
//初始化自顶向下伸展树
public SplayTree()
{
nullNode = new BinaryNode<T>(null);
root = nullNode;
}
/**
* 自顶向下伸展方法
* 最后访问的节点成为新的根
* @param t 要伸展的目标
* @param p 要伸展的子树的根
* @return 伸展后的子树
*/
private BinaryNode<T> splay(T t, BinaryNode<T> p)
{
//定义左树的最大值与右树的最小值
BinaryNode<T> leftTreeMax;
BinaryNode<T> rightTreeMin;
//构建左右树
header.left = header.right = nullNode;
leftTreeMax = rightTreeMin = header;
nullNode.element = t; //保证匹配?
//开始伸展
while(true)
{
int compareResult = t.compareTo(p.element);
if(compareResult < 0)
{
if(t.compareTo(p.left.element) < 0) //左左
//对p进行左左单旋转,转到中树的根
p = rotateWithLeftChild(p);
if(p.left == nullNode) //找到t
break;
//与右树连接,比x大的都在右树
rightTreeMin.left = p;
rightTreeMin = p;
p = p.left;
}
else if(compareResult > 0)
{
if(t.compareTo(p.right.element) > 0) //右右
//对p进行右右单旋转
p = rotateWithRightChild(p);
if(p.right == nullNode) //找到t
break;
//与左树连接,比x小的都在左树
leftTreeMax.right = p;
leftTreeMax = p;
p = p.right;
}
else //p就是要找的x结点
break;
}
//将左中右三个树合并
leftTreeMax.right = p.left; //p的左子树分给左树
rightTreeMin.left = p.right; //右子树分给右树
p.left = header.right;//左树成为p的左子树
p.right = header.left;//右树成为p的右子树
return p;
}
//左左单旋转
private BinaryNode<T> rotateWithLeftChild(BinaryNode<T> k2)
{
BinaryNode<T> k1 = k2.left;
k2.left = k1.right;
k1.right = k2;
return k1;
}
//右右单旋转
private BinaryNode<T> rotateWithRightChild(BinaryNode<T> k1)
{
BinaryNode<T> k2 = k1.right;
k1.right = k2.left;
k2.left = k1;
return k2;
}
//定义一个新节点,在两次不同的插入之间使用
private BinaryNode<T> newNode = null;
//插入
public void insert(T t)
{
//为新节点赋值
if(newNode == null)
newNode = new BinaryNode<T>(null);
newNode.element = t;
if(root == nullNode) //树是空树
{
newNode.left = newNode.right = nullNode;
root = newNode;
}
else
{
root = splay(t, root);
int compareResult = t.compareTo(root.element);
//根比t小
if(compareResult < 0)
{
//根的左子树成为t的左子树,根及其右子树成为t的右子树,新节点成为新根
newNode.left = root.left;
newNode.right = root;
root.left = nullNode;
root = newNode;
}
//根比t大
else if(compareResult > 0)
{
//根的右子树成为t的右子树,根及其左子树成为t的左子树,新节点成为新根
newNode.right = root.right;
newNode.left = root;
root.right = nullNode;
root = newNode;
}
//t与根相等
else
return;
}
//重置新节点
newNode = null;
//System.out.println(root.element.toString());
}
//移除
public void remove(T t)
{
//要移除的元素不存在
if( !contains(t))
return ;
BinaryNode<T> newTree;
//找到要移除的元素,此时元素在根
if(root.left == nullNode) //没有比t小的元素
newTree = root.right;
else //将与要移除的元素差值最小的左子树中的元素作为新的根
{
newTree = root.left;
newTree = splay(t, newTree);
newTree.right = root.right;
}
root = newTree;
}
//最小值
public T findMin()
{
if(isEmpty())
throw new RuntimeException();
//辅助节点
BinaryNode<T> ptr = root;
//找到最小节点
while(ptr.left != nullNode)
ptr = ptr.left;
//访问的伸展过程
root = splay(ptr.element, root);
return ptr.element;
}
//最大值
public T findMax()
{
if(isEmpty())
throw new RuntimeException();
//辅助节点
BinaryNode<T> ptr = root;
//寻找最大节点
while(ptr.right != nullNode)
ptr = ptr.right;
//访问的伸展过程
root = splay(ptr.element, root);
return ptr.element;
}
//判断包含
public boolean contains(T t)
{
if(isEmpty())
return false;
//伸展
root = splay(t, root);
//判断伸展后的根是否是要找的元素
return root.element.compareTo(t) == 0;
}
//清空树
public void makeEmpty()
{
root = nullNode;
}
//判断树是否为空
public boolean isEmpty()
{
return root == nullNode;
}
}
- 自顶向下伸展树是二叉平衡树的一个变种,保持了O(logn)的摊还时间(每次操作的时间复杂度)