1. 程式人生 > 其它 >poj 3984(模板題,bfs)

poj 3984(模板題,bfs)

13.8 平衡二叉樹

如果給定一個數列{1, 2, 3, 4, 5, 6}, 我們生成二叉排序樹的話,我們不難看出來,所有結點都是其父節點的右子節點,就像一條連結串列一樣,那樣,我們的查詢效率將受到影響,甚至不如連結串列(因為我們要額外判斷左子節點是否為空)

基本介紹:

  1. 平衡二叉樹也叫平衡二叉搜尋樹(Self-balancing binary search tree) 又被稱為 AVL樹,可以保證 查詢效率
  2. 特點是:他是一棵空樹或者它的做喲兩個字數的高度的絕對值不超過1,並且左右兩個字數都是一個平衡二叉樹。平衡二叉樹的常用使用方法又 紅黑樹、AVL、替罪羊樹、Treap、伸展樹等

當右子樹的高度 - 左子樹的高度 > 1 時,我們使用左旋轉,意思是,讓根節點變換成右子節點,然後,將原來右子節點的左子樹,放到原來根節點的左子樹的右子樹上。 步驟如下:

左旋轉:

  1. 建立一個新的結點 newNode,使這個新的結點的值等於當前根節點的值
  2. 把新節點的左子樹設定成當前節點的左子樹newNode.left = left
  3. 把新節點右子樹設定成當前節點的右子樹的左子樹newNode.right = root.right.left
  4. 把當前當前結點值換成右子節點的值value = right.value
  5. 把當前結點的左子樹設定成新節點right = right.right
  6. left = newLeft

右旋轉:

  1. 建立一個新的結點newNode ,另這個新的結點的值等於當前結點的值
  2. 把新節點的右節點設定成當前結點的右節點newNode.right = right
  3. 把新節點的左節點設定成當前節點的左節點的右節點newNode.left = left.right
  4. 將當前節點的值設定成左節點的值value = left.value
  5. 把當前節點的右子樹設定成新節點right = newNode
  6. 把當前節點的左子樹設定成左子樹的左子樹left = left.left
package avl;

public class AVLTreeDemo {
    public static void main(String[] args) {
        int[] arr = {4, 3, 6, 5, 7, 8};
        // 建立一個 ACLTree物件
        AVLTree avlTree = new AVLTree();
        // 新增結點
        for (int i = 0; i < arr.length; i++){
            avlTree.add(new Node(arr[i]));
        }
        // 遍歷
        System.out.println("中序遍歷");
        avlTree.infixOrder();
        System.out.println("平衡~~");
        System.out.println("樹的高度="+ avlTree.getRoot().height());
        System.out.println("樹的左子樹的高度="+avlTree.getRoot().left.height());
        System.out.println("樹的右子樹的高度="+avlTree.getRoot().right.height());
        System.out.println("當前的根節點="+avlTree.getRoot().toString());
    }
}
class AVLTree{

    private Node root;

    public Node getRoot() {
        return root;
    }

    // 新增節點
    public void add(Node node){
        if (root == null){
            root = node;
        }else{
            root.add(node);
        }
    }
    // 中序遍歷
    public void infixOrder(){
        if (root != null){
            root.infixOrder();
        } else {
            System.out.println("當前二叉排序樹為空,不能遍歷");
            return;
        }
    }
}
class Node {
    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
    }

    // 返回以當前結點為根節點的樹的高度
    public int height(){
        return Math.max(left == null ? 0 : left.height(), right == null ? 0 :right.height()) + 1;
    }
    // 返回左子樹的高度
    public int leftHeight(){
        if (left == null){
            return 0;
        }
        return left.height();
    }
    // 返回右子樹的高度
    public int rightHeight(){
        if (right == null){
            return  0;
        }
        return right.height();
    }
    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

    // 遞迴新增,新增節點
    public void add(Node node){
        if (node == null){
            return;
        }
        // 判斷傳入的節點的值和當前子樹的根節點的值的關係
        if (node.value < this.value){
            // 如果當前節點的左子樹為空
            if (this.left == null){
                this.left = node;
            } else {
                // 遞迴的向左子樹新增
                this.left.add(node);
            }
        } else {
            if (this.right == null){
                this.right = node;
            } else {
                this.right.add(node);
            }
        }
        // 當新增完一個結點後,如果 右子樹的高度 -左子樹的高度 > 1
        if (rightHeight() - leftHeight() > 1){
            // 如果右子樹的左子樹的高度比右子樹的左子樹的高度要高,先進行一次右旋轉
            if (right != null && right.leftHeight() > right.rightHeight()){
                rightRotate();
            }
            leftRotate(); // 左旋轉
            return; // 要辦然他還要進行一次判斷,防止再給轉回去
        }
        // 當新增完一個節點後,如果 左子樹的高度 - 右子樹的高度 > 1
        if (leftHeight() - rightHeight() > 1){
            // 如果左子樹的右子樹的高度比左子樹的左子樹的高度高,則先進性一次左旋轉
            if (left != null && left.rightHeight() > left.leftHeight()){
                leftRotate();
            }
            rightRotate();
        }
    }
    // 中序遍歷
    public void infixOrder(){
        if (this.left != null){
            this.left.infixOrder();
        }
        System.out.print(this.value+"\t");
        if (this.right != null){
            this.right.infixOrder();
        }
    }

    // 左旋轉的方法
    private void leftRotate(){
        // 建立新的節點,以當前根節點的值
        Node newNode = new Node(value);
        // 把新的結點的左子樹,設定成當前結點的左子樹
        newNode.left = left;
        // 把新的結點右子樹設定成當前結點的右子樹的左子樹
        newNode.right = right.left;
        // 把當前結點的值替換成右子樹的值
        value = right.value;
        // 把當前結點右子樹設定成當前結點右子樹的右子樹
        right = right.right;
        // 把當前結點的左子樹(左子節點)設定成新的結點
        left = newNode;
    }

    // 右旋轉
    private  void rightRotate(){
        // 建立新的節點,以當前根節點的值
        Node newNode = new Node(value);
        // 把新節點右子樹設定成當前結點的右子樹
        newNode.right = right;
        // 把新節點的左子樹這支撐當前結點的左子樹的右子樹
        newNode.left = left.right;
        // 把當前結點的值替換成左子樹的值
        value = left.value;
        // 把當前結點的左子樹設定成左子樹的左子樹
        left = left.left;
        // 把當前結點的右子樹設定成新結點的右子樹
        right = newNode;
    }
}