伸展树-自顶向下
一、 性质
- 把被查找的条目搬移到离树根近一些的地方
- 亦满足二叉树的性质左子树小于根节点,右子树大于根节点
二、 操作
- 伸展操作
splay(x,rt)
- 从中树的根节点开始分裂为3棵树分别为中树、左树、右树,中树按值
x
分裂将大于x
的树节点连接到右树上,将小于x
的树节点连接到左树上 - 分裂时如果是
一字型
的情况需要旋转,以降低树的高度,如果不是则直接分裂 - 最后合并3棵树
- 从中树的根节点开始分裂为3棵树分别为中树、左树、右树,中树按值
- 插入操作
insert(x)
- 如果根节点为空直接插入根节点
- 如果根节点不为空,则使用
x
进行伸展,伸展后的树的左子树
一定小于x
、右子树
一定大于x
- 将
x
插入伸展树的根节点与子树之间(根据根节点的大小决定插入到左子树还是右子树)
- 查找操作
find(x)
- 跟普通二叉树查找一样遍历查找
- 如果
x
在树中,则再执行splay(x,rt)
调整伸展树
- 删除操作
delete(x)
- 执行查找操作,将
x
伸展至根节点 - 如果根节点的左子树为空直接返回右子树即可
- 如果根节点的左子树不为空,将左子树按
x
进行伸展,左子树的右子树一定为空(因为伸展操作后的右子树一定大于x的),此时将根节点的右子树连接到左子树的右子树上
- 执行查找操作,将
- 查找前驱
getPre(x)
- 执行查找操作
find(x)
- 查找根节点左子树最大值(最右节点)
- 执行查找操作
- 查找后驱
getNext(x)
- 执行查找操作
find(x)
- 查找根节点右子树最下值(最左节点)
- 执行查找操作
/*树节点类定义*/
static class Node{
Integer val;
Node left;
Node right;
int size;
Node(Integer val){
this.val = val;
this.size=1;
}
}
/*伸展树 自顶向下 类定义*/
static class SplayTreeDown{
private Node root;//根节点
private Node header = new Node(null);// 头节点 用于存储分裂后的节点
private Node nullNode; // 空节点 减少null的判断技术
/*构造函数*/
public SplayTreeDown(){
nullNode = new Node(null);
nullNode.left = nullNode.right=nullNode;
root = nullNode;
}
/*伸展操作 自顶向下*/
public Node splay(Integer x,Node rt){
//定义左树 和 右树
Node leftMax,rightMin;
header.left = header.right = nullNode;
leftMax = rightMin= header;
nullNode.val = x;
//开始伸展
for(;;){
if(x<rt.val){
if(x<rt.left.val){// 如果是连续小于即一字型的需要旋转以减少树的深度
rt = rotateRight(rt);
}
if(rt.left==nullNode){// 不存在x返回近x为根节点的树
break;
}
rightMin.left = rt;
rightMin = rt; //这里的rt与rt.left的连接没断开 后面的程序会处理掉
rt = rt.left;
}else if(x>rt.val){
if(x>rt.right.val){
rt = rotateLeft(rt);
}
if(rt.right==nullNode){
break;
}
leftMax.right = rt;
leftMax = rt;
rt = rt.right;
}else{
break;
}
}
//合并左树右树和中间树
leftMax.right = rt.left; //这里处理那个未断开的连接
rightMin.left = rt.right;
rt.left = header.right;
rt.right = header.left;
return rt;// 存在x返回以x为根节点的树,不存在x返回近x为根节点的树
}
/*右旋*/
public Node rotateRight(Node rt){
Node left = rt.left;
rt.left = left.right;
left.right = rt;
return left;
}
/*左旋*/
public Node rotateLeft(Node rt){
Node right = rt.right;
rt.right = right.left;
right.left = rt;
return right;
}
/*查询操作 在伸展树中查找元素x。如果x在树中,则再执行Splay(x,S)调整伸展树。*/
public Node find(Integer x){
Node rt = root;
while(rt!=nullNode){
if(x<rt.val){
rt = rt.left;
}else if(x>rt.val){
rt = rt.right;
}else{//找到了
Node re = splay(x,root);
root = re;
return re;
}
}
return nullNode;
}
/*插入操作*/
public void insert(Integer x){
Node rt = root;
//根节点为空 直接插入
if(rt==nullNode){
Node newNode = new Node(x);
root = newNode;
root.left = root.right = nullNode;
}
//根节点 不为空 合并节点
else{
Node newNode = new Node(x);
rt = splay(x,rt);
if(x<rt.val){//根据伸展的操作 rt的左子树一定全部小于x
newNode.left = rt.left;
newNode.right = rt;
rt.left = nullNode;
root = newNode;
}else if(x>rt.val){
newNode.right = rt.right;
newNode.left = rt;
rt.right = nullNode;
root = newNode;
}else{//不支持重复的元素
}
}
}
/*删除操作*/
public void delete(Integer x){
root = splay(x,root);
if(root!=nullNode){
if(root.val==x){
if(root.left==nullNode){
root = root.right;
}else{
Node left = splay(x,root.left);// 伸展操作后 左子树的节点全部小于x
left.right = root.right;
root = left;
}
}
}
}
/*求前驱*/
public Node getPre(Integer x){
Node re = find(x);
if(re!=nullNode){//查询到该节点左子树最大值 即为前驱
Node pre = findMax(re.left);
return pre;
}
return nullNode;
}
/*求后驱*/
public Node getNext(Integer x){
Node re = find(x);
if(re!=nullNode){//查询到该节点左子树最大值 即为前驱
Node next = findMin(re.right);
return next;
}
return nullNode;
}
public Node findMin(Node rt){
while (rt.left!=nullNode){
rt = rt.left;
}
return rt;
}
public Node findMax(Node rt){
while (rt.right!=nullNode){
rt = rt.right;
}
return rt;
}
}