poj 3984(模板題,bfs)
阿新 • • 發佈:2021-08-11
13.8 平衡二叉樹
如果給定一個數列{1, 2, 3, 4, 5, 6}, 我們生成二叉排序樹的話,我們不難看出來,所有結點都是其父節點的右子節點,就像一條連結串列一樣,那樣,我們的查詢效率將受到影響,甚至不如連結串列(因為我們要額外判斷左子節點是否為空)
基本介紹:
- 平衡二叉樹也叫平衡二叉搜尋樹(Self-balancing binary search tree) 又被稱為 AVL樹,可以保證 查詢效率
- 特點是:他是一棵空樹或者它的做喲兩個字數的高度的絕對值不超過1,並且左右兩個字數都是一個平衡二叉樹。平衡二叉樹的常用使用方法又 紅黑樹、AVL、替罪羊樹、Treap、伸展樹等
當右子樹的高度 - 左子樹的高度 > 1 時,我們使用左旋轉,意思是,讓根節點變換成右子節點,然後,將原來右子節點的左子樹,放到原來根節點的左子樹的右子樹上。 步驟如下:
左旋轉:
- 建立一個新的結點
newNode
,使這個新的結點的值等於當前根節點的值 - 把新節點的左子樹設定成當前節點的左子樹
newNode.left = left
- 把新節點右子樹設定成當前節點的右子樹的左子樹
newNode.right = root.right.left
- 把當前當前結點值換成右子節點的值
value = right.value
- 把當前結點的左子樹設定成新節點
right = right.right
left = newLeft
右旋轉:
- 建立一個新的結點
newNode
,另這個新的結點的值等於當前結點的值 - 把新節點的右節點設定成當前結點的右節點
newNode.right = right
- 把新節點的左節點設定成當前節點的左節點的右節點
newNode.left = left.right
- 將當前節點的值設定成左節點的值
value = left.value
- 把當前節點的右子樹設定成新節點
right = newNode
- 把當前節點的左子樹設定成左子樹的左子樹
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; } }