平衡二叉樹(AVL 樹)
1 看一個案例(說明二叉排序樹可能的問題)
給你一個數列{1,2,3,4,5,6},要求建立一顆二叉排序樹(BST), 並分析問題所在.
左邊 BST 存在的問題分析:
1) 左子樹全部為空,從形式上看,更像一個單連結串列.
2) 插入速度沒有影響
3) 查詢速度明顯降低(因為需要依次比較), 不能發揮 BST
的優勢,因為每次還需要比較左子樹,其查詢速度比
單連結串列還慢
4) 解決方案-平衡二叉樹(AVL)
2 基本介紹
1) 平衡二叉樹也叫平衡二叉搜尋樹(Self-balancing binary search tree)又被稱為 AVL 樹, 可以保證查詢效率較高。
2) 具有以下特點:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過 1,並且左右兩個子樹都是一棵
平衡二叉樹。平衡二叉樹的常用實現方法有紅黑樹、AVL、替罪羊樹、Treap、伸展樹等。
3) 舉例說明, 看看下面哪些 AVL 樹, 為什麼?
3 應用案例-單旋轉(左旋轉)
1) 要求: 給你一個數列,建立出對應的平衡二叉樹.數列 {4,3,6,5,7,8}
2) 思路分析(示意圖)
3)程式碼實現
// 左旋轉 private void leftRotate() { // 建立新的節點,以當前根節點的值 SNode newNode = new SNode(value); // 把新的節點左子樹設定成當前節點的左子樹 newNode.left = left; // 把新節點的右子樹設定成當前節點的右子節點的左子樹 newNode.right = right.left; // 把當前節點的值換為右子節點的值 value = right.value; // 把當前節點的右子樹換成右子樹的右子樹 right = right.right; // 把當前節點的左子樹設定成新節點 left = newNode; }
4 應用案例-單旋轉(右旋轉)
1) 要求: 給你一個數列,建立出對應的平衡二叉樹.數列 {10,12, 8, 9, 7, 6}
2) 思路分析(示意圖)
3)程式碼實現
// 右旋轉 private void rightRotate() { SNode newNode = new SNode(value); newNode.right = right; newNode.left = left.right; value = left.value; left = left.left; right = newNode; }
5 應用案例-雙旋轉
前面的兩個數列,進行單旋轉(即一次旋轉)就可以將非平衡二叉樹轉成平衡二叉樹,但是在某些情況下,單旋轉
不能完成平衡二叉樹的轉換。比如數列
int[] arr = { 10, 11, 7, 6, 8, 9 }; 執行原來的程式碼可以看到,並沒有轉成 AVL 樹.
int[] arr = {2,1,6,5,7,3}; // 執行原來的程式碼可以看到,並沒有轉成 AVL 樹
1) 問題分析
2) 解決思路分析
1. 當符號右旋轉的條件時
2. 如果它的左子樹的右子樹高度大於它的左子樹的高度
3. 先對當前這個結點的左節點進行左旋轉
4. 在對當前結點進行右旋轉的操作即可
3) 程式碼實現[AVL 樹的彙總程式碼(完整程式碼)]
package com.lin.avltree_0316; import javax.security.auth.kerberos.KerberosKey; public class AVLTreeDemo { public static void main(String[] args) { // int[] arr = {4, 3, 6, 5, 7, 8}; // int[] arr = {10, 12, 8, 9, 7, 6}; int[] arr = {10, 11, 7, 6, 8, 9}; AVLTree avlTree = new AVLTree(); for (int i = 0; i < arr.length; i++) { avlTree.add(new SNode(arr[i])); } avlTree.infixOrder(); System.out.println("旋轉之後:"); System.out.println("樹的高度:" + avlTree.getRoot().height()); System.out.println("左子樹的高度:" + avlTree.getRoot().leftHeight()); System.out.println("右子樹的高度:" + avlTree.getRoot().rightHeight()); System.out.println("root = " + avlTree.getRoot()); System.out.println("root.left = " + avlTree.getRoot().left); System.out.println("root.left.left = " + avlTree.getRoot().left.left); } } class AVLTree{ private SNode root; // 查詢要刪除的節點 public SNode getRoot() { return root; } public SNode searchDelNode(int value) { if(root == null) { return null; } else { return root.searchDelNode(value); } } // 查詢要刪除節點的父節點 public SNode searchParent(int value) { if(root == null) { return null; } else { return root.searchParent(value); } } /** * @param node 傳入的節點(當作二叉排序樹的根節點) * @return 返回的以node為根節點的二叉排序樹的最小節點的值 */ public int delRightTreeMin(SNode node) { SNode target = node; // 迴圈地查詢左節點,就會找到最小值 while(target.left != null) { target = target.left; } delNode(target.value);// !!!! return target.value;// !!!!! } // 刪除節點 public void delNode(int value) { if(root == null) { return; } else { // 找刪除節點 SNode targetNode = searchDelNode(value); // 沒有找到 if(targetNode == null) { return; } // 如果發現當前這棵二叉樹只有一個節點 if(root.left == null && root.right == null) { root = null; return; } // 去找到targetNode的父節點 SNode parent = searchParent(value); // 如果刪除的節點是葉子節點 if(targetNode.left == null && targetNode.right == null) { // 判斷targetNode是父節點的左子節點還是右子節點 if(parent.left != null && parent.left.value == value) { parent.left = null; } else if(parent.right != null && parent.right.value == value) { parent.right = null; } } else if(targetNode.left != null && targetNode.right != null) { // 有左右子節點 int delRightTreeMin = delRightTreeMin(targetNode.right); targetNode.value = delRightTreeMin; } else {// 只有一個子節點 // 要刪除的節點只有左節點 if(targetNode.left != null) { if(parent != null) { // 如果targetNode是parent的左子節點 if(parent.left.value == value) { parent.left = targetNode.left; } else { parent.right = targetNode.left; } } else { root = targetNode.left; } } else {// 要刪除的節點有右子節點 if(parent != null) { if(parent.left.value == value) { parent.left = targetNode.right; } else { parent.right = targetNode.right; } } else { root = targetNode.right; } } } } } // 中序遍歷 public void infixOrder() { if(root == null) { System.out.println("空樹!"); } else { root.infixOrder(); } } // 新增 public void add(SNode node) { if(root == null) { root = node; } else { root.add(node); } } } class SNode{ protected int value; protected SNode left; protected SNode right; public SNode(int value) { // TODO Auto-generated constructor stub this.value = value; } // 返回左子樹的高度 public int leftHeight() { if(left == null) { return 0; } return left.height(); } // 返回右子樹的高度 public int rightHeight() { if(right == null) { return 0; } return right.height(); } // 返回當前節點的高度,以該節點為根節點的樹的高度 public int height() { return Math.max(left == null ? 0: left.height(), right == null ? 0 : right.height()) + 1; } // 左旋轉 private void leftRotate() { // 建立新的節點,以當前根節點的值 SNode newNode = new SNode(value); // 把新的節點左子樹設定成當前節點的左子樹 newNode.left = left; // 把新節點的右子樹設定成當前節點的右子節點的左子樹 newNode.right = right.left; // 把當前節點的值換為右子節點的值 value = right.value; // 把當前節點的右子樹換成右子樹的右子樹 right = right.right; // 把當前節點的左子樹設定成新節點 left = newNode; } // 右旋轉 private void rightRotate() { SNode newNode = new SNode(value); newNode.right = right; newNode.left = left.right; value = left.value; left = left.left; right = newNode; } @Override public String toString() { // TODO Auto-generated method stub return "Node = [value = " + value + "]"; } // 新增節點 public void add(SNode 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() > rightHeight() ) ) { right.rightRotate(); leftRotate(); } else { leftRotate(); } return;//!!!! } if ((leftHeight() - rightHeight()) > 1) { // 如果當前節點的左子樹的右子樹高度大於左子樹的高度 if (left != null && (left.rightHeight() > left.leftHeight())) { // 先對當前節點的左節點進行左旋轉 left.leftRotate(); // 再對當前節點進行右旋轉 rightRotate(); } else { rightRotate(); } } } // 中序遍歷 public void infixOrder() { if(this.left != null) { this.left.infixOrder(); } System.out.println(this); if(this.right != null) { this.right.infixOrder(); } } // 查詢要刪除的節點 public SNode searchDelNode(int value) { if(this.value == value) { return this; } else if(this.value > value) { // 如果左子節點為空 if(this.left == null) { return null; } return this.left.searchDelNode(value); } else { if(this.right == null) { return null; } return this.right.searchDelNode(value); } } // 查詢要刪除節點的父節點, 如果沒有則返回null public SNode searchParent(int value) { if(( this.left != null && this.left.value == value) || ( this.right != null && this.right.value == value )) { return this; } else { // 如果查詢的值小於當前節點的值,並且當前節點的左子節點不為空 if(value < this.value && this.left != null) { return this.left.searchParent(value); } else if(value >= this.value && this.right != null) { return this.right.searchParent(value); } else { return null; } } } }
僅供參考,有錯誤還請指出!
有什麼想法,評論區留言,互相指教指教。
覺得不錯的可以點一下右邊的推薦喲