平衡二叉樹(AVL樹)
阿新 • • 發佈:2020-11-04
為什麼要在二叉搜尋樹的基礎上提出平衡二叉樹?
考慮這樣一種情況:
當我們的二叉搜尋樹結構如圖所示時,
這棵樹與單鏈表的查詢,刪除時間複雜度相比相差無幾,甚至高於單鏈表(二叉搜尋樹還需要判斷左子樹)
為了避免上述不平衡的儲存結構出現
以及保證較高的查詢效率,提出了平衡二叉樹
平衡二叉樹定義:任意節點的子樹的高度差都小於等於1
基於Java實現平衡二叉搜尋樹(AVL樹)
為了減少文章篇幅,本文基於上篇部落格的二叉樹程式碼完成AVL樹的構建,並主要講解平衡二叉樹的旋轉問題。
關於二叉排序樹的詳細構建,程式碼見:二叉排序樹
首先實現getHeight方法得到當前樹的高度和其子樹的高度:
//返回根結點的高度public int height(){ return Math.max(left == null ? 0 : left.height(),right == null ? 0 : right.height())+1; }
遞迴思想解釋:以根結點左結點和右節點為初始遞迴條件,一直往下遞迴到葉子結點,往上每返回一層遞迴函式高度加一,比較左右結點路徑的深度,得到最大深度
獲得當前結點的左子結點和右子結點深度的思想類似:
//返回左子樹的高度 public int LeftHight(){ if(left == null){return 0; }else{ return left.height(); } }
//返回右子樹的高度 public int rightHeight() { if(right==null){ return 0; }else{ return right.height(); } }
接著考慮一般情況下的左旋和右旋情形:
左旋:該樹滿足所有結點的右側樹高度均大於左側樹高度
由於右邊比左邊高,所以很容易想到將根節點從5移動到8,對結點8周圍的三條連線線做處理,為了滿足二叉樹的定義,
將6接到結點5上。而此時如果為了得到高度相同的左右子樹而直接修改根節點,會涉及到大量的修改。
所以為了方便起見,在右樹中刪除一個結點,在左樹中新增一個結點
左旋程式碼實現:
public void leftRotate(){ //建立新的結點儲存資訊 Node newNode = new Node(value); newNode.left = left; newNode.right = right.left; value = right.value; right = right.right; left = newNode; }
單次右旋思路一致:所有結點的左子樹高度大於其右子樹高度即單次右旋
public void rightRotate(){ Node newNode = new Node(value); newNode.right = right; newNode.left = left.right; value = left.value; left = left.left; right = newNode; }
接著考慮雙旋轉問題的解決:
此時通過單次左旋無法使樹恢復平衡狀態,可以單看結點11,結點11的左子樹高度為2,右子樹為0,剛好與整體樹失衡的方向相反,
那麼我們對結點11做一次右旋再對樹整體做一次左旋即可完成平衡二叉樹的構建。
if(rightHeight()-LeftHight() > 1){ if(left != null && left.LeftHight() > left.rightHeight()){ left.rightRotate(); leftRotate(); }else { leftRotate(); } }
對以上方法進行總結,並將其新增到add方法中:因為每新增一個新資料就需要判斷樹當前是否失衡。
整體add方法如下:
public void add(Node node){ if(node == null){ return; } if(this.value > node.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); } } //右旋 if(LeftHight()-rightHeight() > 1){ if(right != null && left.rightHeight() > left.LeftHight()){ left.leftRotate(); rightRotate(); }else { rightRotate(); } return; } //左旋 if(rightHeight()-LeftHight() > 1){ if(left != null && left.LeftHight() > left.rightHeight()){ left.rightRotate(); leftRotate(); }else { leftRotate(); } } }