這兩天寫的一個二叉平衡樹

BianFuu發表於2020-10-15

這兩天寫的一個二叉平衡樹

簡介

  • 提供了插入、刪除、查詢節點是否存在、獲取樹的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一定是葉子節點

        //    被刪除節點是根節點
        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 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);
                }
            }
        }
    }

    //    查詢插入節點的父節點(根據插入節點的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);
    }

}

相關文章