伸展树-自顶向下

一、 性质

  • 把被查找的条目搬移到离树根近一些的地方
  • 亦满足二叉树的性质左子树小于根节点,右子树大于根节点

二、 操作

  • 伸展操作splay(x,rt)
    1. 从中树的根节点开始分裂为3棵树分别为中树、左树、右树,中树按值x分裂将大于x的树节点连接到右树上,将小于x的树节点连接到左树上
    2. 分裂时如果是一字型的情况需要旋转,以降低树的高度,如果不是则直接分裂
    3. 最后合并3棵树
  • 插入操作insert(x)
    1. 如果根节点为空直接插入根节点
    2. 如果根节点不为空,则使用x进行伸展,伸展后的树的左子树一定小于x右子树一定大于x
    3. x插入伸展树的根节点与子树之间(根据根节点的大小决定插入到左子树还是右子树)
  • 查找操作find(x)
    1. 跟普通二叉树查找一样遍历查找
    2. 如果x在树中,则再执行splay(x,rt)调整伸展树
  • 删除操作delete(x)
    1. 执行查找操作,将x伸展至根节点
    2. 如果根节点的左子树为空直接返回右子树即可
    3. 如果根节点的左子树不为空,将左子树按x进行伸展,左子树的右子树一定为空(因为伸展操作后的右子树一定大于x的),此时将根节点的右子树连接到左子树的右子树上
  • 查找前驱getPre(x)
    1. 执行查找操作find(x)
    2. 查找根节点左子树最大值(最右节点)
  • 查找后驱getNext(x)
    1. 执行查找操作find(x)
    2. 查找根节点右子树最下值(最左节点)
/*树节点类定义*/
    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;
        }
    }
posted @ 2022-11-09 15:17  tongyongliang  阅读(36)  评论(0编辑  收藏  举报