- 伸展树与二叉平衡查找树
AVL树(带有平衡条件的树)查找的时候最坏的情形为0(N)并不坏,只要它不经常发生就可以。任何一次访问,即使花费0(N),任然可能非常快。具有最坏运行时间0(N)但保证对任意M次连续操作最多花费O(MlogN)运行时间确实让人满意了。
伸展树和其它树的区别就是没访问一个节点的时候,那个节点就会成为根节点,这样下次访问的时候时间为0(1)并且与二叉平衡查找树比少了高度的属性和判断平衡信息,因此从某种程度上简化代码。
在伸展树中最主要的一部分就是伸展,分为两种情况第一种是之字形(zig-zag)情形,第二种就是一字形(zig-zig)
package tree;
/*
* 伸展树:他保证从空树开始连续M次对树的操作最多花费0(MlogN)时间
*/
public class STree<AnyType extends Comparable<? super AnyType>>{
private Node root;
private class Node{
public Node left;//左孩子
public Node right;//右孩子
public Node parent;//父亲节点
public AnyType x;//data数据
public Node(AnyType x){
this.x = x;
parent = null;
left = null;
right = null;
}
}
public STree(){
root = null;
}
//查找
public AnyType find(AnyType key){
root = find(root, key);
if(root == null || root.x.compareTo(key) != 0) return null;
return root.x;
}
private Node find(Node p, AnyType key){
if(p == null || key == null) return null;
Node prep = null;
while(p != null){
prep = p;
int cmp = key.compareTo(p.x);
if(cmp < 0) p = p.left;
else if(cmp > 0) p = p.right;
else break;
}
root = splay(prep);
return root;
}
//插入
public void insert(AnyType data){
if(data == null) return;
if(root == null){
root = new Node(data);
return;
}
Node t = find(root, data);
int cmp = data.compareTo(t.x);
if(cmp == 0){//更新value
root.x = data;
return;
}
root = new Node(data);
//先把T分为左右子树,再合并
if(cmp < 0){//插入新根,以t->lchild和t为左/右孩子
Node x = t.left;
root.left = x;
root.right = t;
t.parent = root;
if(x != null){
x.parent = root;
t.left = null;
}
}else if(cmp > 0){
Node x = t.right;
root.left = t;
root.right = x;
t.parent = root;
if(x != null){
x.parent = root;
t.right = null;
}
}
}
//删除
public void delect(AnyType key){
if(key == null) return;
Node t = find(root, key);
if(t == null || key.compareTo(t.x) != 0)
return;
if(t.right == null){//右子树为空
root = root.left;
root.parent = null; return;
}
//右子数不空
//1.删除T节点, 得到子树tl, tr
Node tl = t.left, tr = t.right;
tr.parent = null;
//2.将右子树最小节点min伸展至右子树树根,min一定没有左子节点
Node min = min(tr);
//3.将tl作为左子树链接到min上
min.left = tl;
if(tl != null) tl.parent = min;
root = min;
}
public AnyType min(){
if(root == null) return null;
root = min(root);
return root.x;
}
private Node min(Node x){
if(x == null) return null;
while(x.left != null)
x = x.left;
x = splay(x);
return x;
}
public AnyType max(){
if(root == null) return null;
root = max(root);
return root.x;
}
private Node max(Node x){
if(x == null) return null;
while(x.right != null)
x = x.right;
x = splay(x);
return x;
}
private Node splay(Node v){//v是找到的节点
if(v == null) return null;
Node p, g; //v的父亲与祖父
//自上而下,反复对v做双层伸展
while((p = v.parent) != null && (g = p.parent) != null){
Node gg = g.parent;//(great-grand parent)为父
if(isLChild(v)){
if(isLChild(p)){//zig-zig(g)先g结点右旋,zig(p)在p右旋
attachAsLChild(g, p.right);
attachAsRChild(p, g);
attachAsLChild(p, v.right);
attachAsRChild(v, p);
}else{//zig-zag(p)先p右旋,再g左旋zag(g)
attachAsLChild(p, v.right);
attachAsRChild(v, p);
attachAsRChild(g, v.left);
attachAsLChild(v, g);
}
}else{
if(isRChild(p)){//zag-zag,zag(g)先g结点左旋,zig(p)再p左旋
attachAsRChild(g, p.left);
attachAsLChild(p.left, g);
attachAsRChild(p, v.left);
attachAsLChild(v, p);
}else{//zag-zig,先p左旋,zag(p),再g右旋zig(g)
attachAsRChild(p, v.left);
attachAsLChild(v, p);
attachAsLChild(g, v.right);
attachAsRChild(v, g);
}
}
//若v原先的曾祖父gg不存在, 则v现在应为树根
if(gg == null) v.parent = null;
else{//否则,gg此后应该以v作为左或右孩子
if(gg.left == g) attachAsLChild(gg, v);
else attachAsRChild(gg, v);
}
}
//双层伸展结束时,必有g == null, 但p可能非空
//若p果真非空,则额外在做一次单旋
if(p != null && p == v.parent){
if(isLChild(v)){//zig
attachAsRChild(p, v.right);
attachAsRChild(v, p);
}else{//zag
attachAsRChild(p, v.left);
attachAsLChild(v, p);
}
}
v.parent = null;
return v;
}
//判断n是否是父节点的左孩子
private boolean isLChild(Node n){
Node p = n.parent;
return p.left == n;
}
//判断n是否是父节点的右孩子
private boolean isRChild(Node n){
Node p = n.parent;
return p.right == n;
}
//在节点p与rc(可能为空)之间建立父(左)子关系
private void attachAsLChild(Node parent, Node lchild){
parent.left = lchild;
if(lchild != null)
lchild.parent = parent;
}
//在节点p与rc(可能为空)之间建立父(右)子关系
private void attachAsRChild(Node parent, Node rchild){
parent.right = rchild;
if(rchild != null)
rchild.parent = parent;
}
public void prearder(){
prearder(root);
System.out.println("前序遍历");
System.out.println();
}
private void prearder(Node p){
if(p == null) return;
System.out.print(p.x+" ");
prearder(p.left);
prearder(p.right);
}
//测试
public static void main(String[] args){
STree<Integer> st = new STree<Integer>();
for(int i=0; i<10; i++)
st.insert(i);
st.find(5);
st.prearder();
}
}