JAVA之伸展树

  • 伸展树与二叉平衡查找树
    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();
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值