这两天写的一个二叉平衡树

这两天写的一个二叉平衡树

简介
  • 提供了插入、删除、查询节点是否存在、获取树的size、获取迭代器的方法和一个层序遍历的方法
重点:
  • 下面平衡树使用的节点中有一个balance属性,记录一个节点是否平衡,不平衡的话是左子树高还是右子树高,高多少,balance = 右子树高度减去左子树高度(可以是-1,0,1)
  • 还有一个side属性,用于记录一个节点是父节点的左子节点(-1)还是右子节点(1),还是根节点(0)
下面是源代码,尽量写了清晰的注释
//E  必须要求要  有 hashCode()方法,没有的话,就会调用Object的hashCode()方法,那就大家都相等了
public class Banlance_Tree<E> implements Iterable<E> {

    private Node root = null;    //    整颗树的根节点
    private int size;            //树中的节点个数

    public int size() {
        return size;
    }

    //    内部类
    private class Node {
        private Node parent;
        private Node left;
        private Node right;
        private E val;

        private int balance;         //作为每一个节点的平衡因子(等于右子树高度减去左子树高度),(可以是-1,0,1)
        private int side;            //记录当前节点是父节点的左子节点(-1)还是右子节点(1),当前节点是根节点(0)

        //  构造方法一
        private Node(E val, int balance, int side) {
            this.val = val;
            this.balance = balance;
            this.side = side;
        }

        //  构造方法二
        private Node(E val, Node parent, int balance, int side) {
            this.val = val;
            this.parent = parent;
            this.balance = balance;
            this.side = side;
        }

        @Override
        public String toString() {
            String str1 = parent == null ? "null" : String.valueOf(parent.val);
            String str2 = left == null ? "null" : String.valueOf(left.val);
            String str3 = right == null ? "null" : String.valueOf(right.val);
            return "Node{" +
                    "parent=" + str1 +
                    ", left=" + str2 +
                    ", right=" + str3 +
                    ", val=" + val +
                    ", balance=" + balance +
                    ", side=" + side +
                    '}';
        }
    }

    //    查询节点是否存在
    public boolean isExit(E val) {
        Node node = findNode(val);
        return node != null && node.val.equals(val);      //找到node!=null只能说明两者的hashCode相等,不能说明两者是equals的
    }

    //    插入操作
    public boolean insert(E val) {

        //  如果该树为空
        if (root == null) {
            size++;
            root = new Node(val, 0, 0);
            return true;
        }
        //    找到要插入位置的父节点
        Node father = findFatherNode(val);
        //    插入节点
        if (val.hashCode() == father.val.hashCode()) {      //树中存在相同 val的节点,不用插入
            return false;
        }

        //    往下就是一定会插入,那么先使 size 自增
        size++;

        if (val.hashCode() > father.val.hashCode()) {        //作为右子节点插入
            father.right = new Node(val, father, 0, 1);
            father.balance++;
        } else {     //作为左子节点插入
            father.left = new Node(val, father, 0, -1);
            father.balance--;
        }

        //      处理旋转操作
        while (true) {
            int demoside;
            demoside = father.side;      //先记录father的side值
            if (father.balance == 0 || father.side == 0) break;       //说明插入节点有兄弟节点,那就不会导致树的不平衡,即不用斗鱼处理了

            //     让father指向插入节点的祖父节点
            father = father.parent;
            //     处理祖父节点的balance
            if (demoside == -1) {             //当前节点作为父节点的左节点
                father.balance--;
            } else if (demoside == 1) {       //当前节点作为父节点的右节点     //这里的判断条件可以省略吗?
                father.balance++;
            }

            if (father.balance == 0) {              //这一段if else 可以搬到上面的if else 里面去
                break;
            } else if (father.balance == -2) {
                if (father.left.balance == -1) {      //左左情况(左子树的左子树造成了father节点的不平衡)
                    right_roration(father);
                } else {
                    left_roration(father.left);      //先对该节点的左子节点左旋
                    right_roration(father);         //再对该节点右旋
                }
            } else if (father.balance == 2) {
                if (father.right.balance == 1) {
                    left_roration(father);
                } else {
                    right_roration(father.right);
                    left_roration(father);
                }
            }
        }
        return true;
    }

    //    删除操作
    public boolean delete(E val) {
        //   找到该节点
        Node node = findNode(val);
        if (node == null) return false;      //节点没找到

        //往下就是要删除的节点找到了
        size--;

        //   记录被删除节点
        Node rem = node;

        //   该节点不是叶子节点
        //   找到要删除的叶子节点
        if (node.left != null) {
            node = node.left;
            while (node.right != null) {
                node = node.right;
            }
        } else if (node.right != null) {
            node = node.right;
            while (node.left != null) {
                node = node.left;
            }
        }

        //    用该叶子节点顶替被删除节点的位置
        rem.val = node.val;
        deleteLeaf(node);
        return true;
    }

    //    删除叶子节点
    private void deleteLeaf(Node node) {
        //    首先要明确,能走到这里,被删除节点node一定是不为null的,且node一定是叶子节点

        Node demo = null;      //删除节点 进行旋转操作后使得 某个节点回复平衡 后,需要记录其父节点,
        int side = 0;          //因为其父节点的balance一定被改变了,那么其父节点也可能会失去平衡

        //    被删除节点是根节点
        if (node.side == 0) {
            root = null;
            return;
        }

        //    找到被删除节点的父节点
        Node father = node.parent;
        //    删除节点
        if (node.side == -1) {
            father.left = null;
            father.balance++;
        } else {
            father.right = null;
            father.balance--;
        }

        //  处理旋转操作
        while (true) {
            if (father.balance == 0) {      //以father为根节点的树的深度减少了一层,那么继续往上找
                if (father.side == 0) {      //该节点为根节点
                    break;
                }
                if (father.side == -1) {       //该节点是父节点的左子节点
                    father = father.parent;
                    father.balance++;
                } else {                       //该节点是父节点的右子节点
                    father = father.parent;
                    father.balance--;
                }
                continue;
            }
            if (father.balance == -1 || father.balance == 1) {  //如果balance变成了1或者-1,那么balance原来一定是0,那么由0变成1或者-1是不会改变该节点的深度的,也就不可能破会树的平衡了
                break;
            } else {
//            到这里就说明节点father的平衡被打破了,需要旋转
//                先记录下father.parent节点,因为对节点father进行旋转操作一定会使得father.parent的balance改变
                demo = father.parent;
                side = father.side;
                if (father.balance == -2) {
                    assert father.left != null : "当前节点的balance为-2,而此时当前节点没有左子节点";      // 用一下断言,实际上是不可能出现一个节点的balance为-2,而又没有左子节点的
                    if (father.left.balance == -1) {      //左左情况(左子树的左子树造成了father节点的不平衡)
                        right_roration(father);         //右旋
                    } else {
                        left_roration(father.left);      //先对该节点的左子节点左旋
                        right_roration(father);         //再对该节点右旋
                    }
                } else /*if (father.balance == 2) */{
                    assert father.right != null : "当前节点的balance为-2,而此时当前节点没有右子节点";      // 用一下断言,实际上是不可能出现一个节点的balance为-2,而又没有右子节点的
                    if (father.right.balance == 1) {
                        left_roration(father);
                    } else {
                        right_roration(father.right);
                        left_roration(father);
                    }
                }

//                到这里,当前子树的平衡就恢复了, 但由于 旋转操作使得当前子树的 深度 减少了一层 ,所以要继续处理该子树的父节点
                father = demo;
                if (father != null) {
                    if(side == -1){
                        father.balance ++;
                    }else{
                        father.balance --;
                    }
                }else{
                    break;
                }
            }
        }

    }

    //    查找插入节点的父节点(根据插入节点的val查找)(如果val以存在,返回值为val的节点)
    private Node findFatherNode(E val) {
        Node demo = root;
        Node oc = null;
        while (demo != null) {
            oc = demo;
            if (val.hashCode() < demo.val.hashCode()) {      //往左走
                demo = demo.left;
            } else if (val.hashCode() > demo.val.hashCode()) {      //往右走
                demo = demo.right;
            } else {
                break;
            }
        }
        return oc;
    }

    //    查找节点
    private Node findNode(E val) {
        Node fatherNode = findFatherNode(val);
        if (fatherNode != null && fatherNode.val.hashCode() == val.hashCode()) {
            return fatherNode;
        }
        return null;
    }


    //这里的①②③④⑤是表示对节点①进行旋转会影响到的点或周围的点,不是说树就长这样
/*
            ①                  ②
          ↙   ↘               ↙   ↘
        ②     ③     ==>    ④     ①
      ↙   ↘                      ↙   ↘
    ④     ⑤                  ⑤      ③
                                                */
    //右旋(左左)
    private void right_roration(Node node) {
        //node 是 ①

        Node father = node.parent;      //记录node的父节点
        int side = node.side;      //记录node的side

        Node demo = node.left.right;    //demo = ⑤
        node.left.right = node;         //②.right = ①
        node.left.parent = node.parent; //②.parent = ①.parent
        node.parent = node.left;        //①.parent = ②

        node.left = demo;               //①.left = ⑤
        if (demo != null) {
            demo.parent = node;         //⑤.parent = ①
            demo.side = -1;
        }

        //   处理①和②的balance,①一定会由-2变为0,②一定会由-1变为0
        node.balance = 0;
        node.parent.balance = 0;

        //    处理①和②的side
        node.parent.side = node.side;
        node.side = 1;

        //    原来①为该子树的根节点,现在要改为②
        if (side == 1) {    //原根节点是原根节点父节点的右节点
            father.right = node.parent;
        } else if (side == -1) {   //原根节点是原根节点父节点的左节点
            father.left = node.parent;
        } else {             //原根节点没有父节点,那直接让node.parent作为root节点
            root = node.parent;
        }
    }

    /*
                ①                     ③
              ↙   ↘                  ↙   ↘
            ②     ③     ==>       ①     ⑤
                 ↙   ↘            ↙   ↘
               ④     ⑤         ②     ④
                                                    */
    //左旋(右右)
    private void left_roration(Node node) {

        Node father = node.parent;
        int side = node.side;

        Node demo = node.right.left;
        node.right.left = node;
        node.right.parent = node.parent;
        node.parent = node.right;

        node.right = demo;
        if (demo != null) {
            demo.parent = node;
            demo.side = 1;
        }

        node.balance = 0;
        node.parent.balance = 0;

        node.parent.side = node.side;
        node.side = -1;


        if (side == 1) {
            father.right = node.parent;
        } else if (side == -1) {
            father.left = node.parent;
        } else {
            root = node.parent;
        }
    }

    //    实现迭代器
    //      这里迭代器按中序遍历来(反正是对比的hashCode,实际上这个中序遍历时没有什么意义的)
    private class InnerIterator implements Iterator<E> {
        private List<E> list = new ArrayList<>();
        private int index;

        private InnerIterator() {
            non_recursion(Banlance_Tree.this.root);
        }

        @Override
        public boolean hasNext() {
            return index < list.size();
        }

        @Override
        public E next() {
            if (hasNext()) return list.get(index++);
            return null;
        }

        @Override
        public void remove() {
            index++;
        }

        private void non_recursion(Node root) {
            list = new ArrayList<>();
            Deque<Node> deque = new LinkedList<>();
            while (root != null || !deque.isEmpty()) {
                while (root != null) {
                    deque.push(root);
                    root = root.left;
                }
                root = deque.pop();
                list.add(root.val);
                root = root.right;
            }
        }
    }

    @Override
    public Iterator<E> iterator() {
        return new InnerIterator();
    }

    //    层次遍历
    public void level_traverse() {
        ArrayList<E> oc = new ArrayList<>();
        Deque<Node> deque = new LinkedList<>();
        Node trav = root;
        deque.offer(trav);
        while (!deque.isEmpty()) {
            trav = deque.poll();
            if (trav == null) continue;
            oc.add(trav.val);
            deque.offer(trav.left);
            deque.offer(trav.right);
        }
        System.out.println(oc);
    }

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值