Java實現平衡二叉樹(AVLTree)的構建
阿新 • • 發佈:2018-12-25
最近在學習資料結構上關於平衡二叉樹的知識,看了嚴老師的思路,感覺用java寫出遞迴的構建方式有點困難,因為其中的遞迴需要把引用傳進去,所以感覺是要實現起來比較麻煩,所以就首先想到使用非遞迴的方式來實現構建平衡二叉樹。
使用非遞迴的方式,思路也很簡單,就是為每一個結點都要定義一個平衡因子的屬性,當成功向樹中插入一個數據時,我就要進行回溯,看看有沒有平衡因子的絕對值等於2的結點,如果有,那就需要進行旋轉。當時的思路僅限於這些,接觸java沒有多久,目測如果實現起來,有點困難。
其中,我認為還有一個難題就是,我定義的結點使用了泛型,即結點的value也是使用的泛型<E>,所以在比較大小的時候,我就無從下手,可能是我接觸java不太久的原因,當我參考上述部落格的思路後,居然是這樣實現的。
void compare(E element, Node node){
Comparable<? super E> e = (Comparable<? super E>)element;
int cmp = e.compareTo(node.element);
if(cmp > 0) //大於
else if(cmp == 0) //等於
else //小於
}
通過此次學習,雖然接觸了平衡二叉樹的構建,但還是感覺學到了不少的知識,並且對java的使用又有了更多的認識。
有了上述的感悟,感覺還是寫一個部落格來收藏一下,等自己有時間,再補充一下結點刪除的程式碼。
可能本人程式碼在註釋上提供的思路有限,如果有看不懂的地方,可以參考上面的部落格地址的內容。
package util; /** * 平衡二叉樹 * 定義:首先它是一種特殊的二叉排序樹,其次它的左子樹和右子樹都是平衡二叉樹, * 且左子樹和右子樹的深度之差不超過1 * 平衡因子:可以定義為左子樹的深度減去右子樹的深度 * * 平衡二叉樹是對二叉排序樹的優化,防止二叉排序樹在最壞情況下平均查詢時間為n, * 二叉排序樹在此時形如一個單鏈表 * 平衡二叉樹查詢元素的次數不超過樹的深度,時間複雜度為logN */ public class AVLTree<E> { /**根節點*/ private Node<E> root = null; /**樹中元素的個數*/ private int size = 0; private static final int LEFT_HIGH = 1; private static final int RIGHT_HIGH = -1; private static final int EQUAL_HIGH = 0; public AVLTree(){} public boolean insertElement(E element){ Node<E> t = root; if(t == null){ /**將值複製給根節點,其父節點為空*/ root = new Node(element, null); size = 1; return true; } int cmp = 0; Node<E> parent; /**用來儲存t的父節點*/ /**查詢結點應存放的位置*/ Comparable<? super E> e = (Comparable<? super E>)element; do{ parent = t; cmp = e.compareTo(t.element); if(cmp < 0){ t = t.left; }else if(cmp > 0){ t = t.right; }else{ return false; } }while(t != null); /**將結點存放在相應的位置*/ Node<E> child = new Node(element, parent); if(cmp < 0){ parent.left = child; }else{ parent.right = child; } /**開始回溯,為根節點修改balabce,以便進行相應的調整*/ while(parent != null){ cmp = e.compareTo(parent.element); if(cmp < 0){ parent.balance ++; }else{ parent.balance --; } if(parent.balance == 0){/**從這以上的結點都不需要修改了,也不需要旋轉了*/ break; } if(Math.abs(parent.balance) == 2){ fixAfterInsertion(parent); break; } parent = parent.parent; } size ++; return true; } private void fixAfterInsertion(Node<E> p) { if(p.balance == 2){ leftBanance(p); } if(p.balance == -2){ rightBalance(p); } } /** * 左平衡操作,即結點t的不平衡是因為左子樹過深 * * 1、如果新的結點插入到p的左孩子的左子樹中,則直接進行右旋操作即可 * t lc * / \ 右旋操作 / \ * lc rc -------------> lcl t * / \ / / \ * lcl lcr lcll lcr rc * / * lcll * * 2、如果新的結點插入到p的左孩子的右子樹中,則需要進行分情況討論 * * 情況a:當p的左孩子的右子樹根節點的balance = RIGHT_HIGH * * 1 1 4 * / \ / \ / \ * 2 6 左旋 4 6 右旋 2 1 * / \ -------> / \ --------> / / \ * 3 4 2 5 3 5 6 * \ / * 5 3 * * * 情況b:當p的左孩子的右子樹根節點的balance = LEFT_HIGH * * 1 1 4 * / \ / \ / \ * 2 6 左旋 4 6 右旋 2 1 * / \ -------> / --------> / \ \ * 3 4 2 3 5 6 * / / \ * 5 3 5 * * 情況c:當p的左孩子的右子樹根節點的balance = EQUAL_HIGH * * 1 1 4 * / \ / \ / \ * 2 7 左旋 4 7 右旋 2 1 * / \ -------> / \ --------> / \ / \ * 3 4 2 6 3 5 6 7 * / \ / \ * 5 6 3 5 * */ private void leftBanance(Node<E> t) { Node<E> lc = t.left; switch(lc.balance){ case LEFT_HIGH: /**新結點插入到t的左孩子的左子樹上,需要單右旋處理*/ lc.balance = EQUAL_HIGH; t.balance = EQUAL_HIGH; break; case RIGHT_HIGH: /**新結點插入到t的左孩子的右子樹上,需要雙旋處理*/ Node<E> rd = lc.right; switch(rd.balance){ case LEFT_HIGH: lc.balance = EQUAL_HIGH; t.balance = RIGHT_HIGH; break; case RIGHT_HIGH: lc.balance = LEFT_HIGH; t.balance = EQUAL_HIGH; break; case EQUAL_HIGH: t.balance = EQUAL_HIGH; lc.balance = EQUAL_HIGH; break; } rd.balance = EQUAL_HIGH; /**對t的左子樹進行左旋處理*/ left_Rotate(t.left); /**對t進行右旋處理*/ right_Rotate(t); break; } } /** * 右平衡操作,即結點t的不平衡是因為右子樹過深 * * 1、如果新的結點插入到p的右孩子的右子樹中,則直接進行左旋操作即可 * * p r * / \ / \ * l r 左旋操作 p rr * / \ -----------> / \ \ * rl rr l rl rrr * \ * rrr * * * 2、如果新的結點插入到p的右孩子的左子樹中,則需要進行分情況討論 * * 情況a:當p的右孩子的左子樹根節點的balance = LEFT_HIGH * * 1 1 4 * / \ / \ / \ * 2 3 右旋 2 4 左旋 1 3 * / \ -------> / \ -------> / \ \ * 4 5 6 3 2 6 5 * / \ * 6 5 * * 情況b:當p的右孩子的左子樹根節點的balance = RIGHT_HIGH * * 1 1 4 * / \ / \ / \ * 2 3 右旋 2 4 左旋 1 3 * / \ -------> \ -------> / / \ * 4 5 3 2 6 5 * \ / \ * 6 6 5 * * * 情況C:當p的右孩子的左子樹根節點的balance = EQUAL_HIGH * 1 1 4 * / \ / \ / \ * 2 3 右旋 2 4 左旋 1 3 * / \ -------> / \ -------> / \ / \ * 4 5 6 3 2 6 7 5 * / \ / \ * 6 7 7 5 * */ private void rightBalance(Node<E> p) { Node<E> rc = p.right; switch(rc.balance){ case RIGHT_HIGH: /**新結點插入到t的右孩子的右子樹上,需要單左旋處理*/ rc.balance = EQUAL_HIGH; p.balance = EQUAL_HIGH; break; case LEFT_HIGH: /**新結點插入到t的右孩子的左子樹上,需要雙旋處理*/ Node<E> ld = rc.left; switch(ld.balance){ case LEFT_HIGH: p.balance = EQUAL_HIGH; rc.balance = RIGHT_HIGH; break; case RIGHT_HIGH: p.balance = LEFT_HIGH; rc.balance = EQUAL_HIGH; break; case EQUAL_HIGH: p.balance = EQUAL_HIGH; rc.balance = EQUAL_HIGH; break; } ld.balance = EQUAL_HIGH; /**對p的右子樹進行右旋處理*/ right_Rotate(p.right); /**對p進行左旋處理*/ left_Rotate(p); break; } } /** * 左旋操作 * p r * / \ / \ * l r 左旋操作 p rr * / \ -----------> / \ \ * rl rr l rl rrr * \ * rrr * */ private void left_Rotate(Node<E> p) { if(p != null){ Node<E> r = p.right; /**獲得p的右子樹的根節點r*/ p.right = r.left; /**將r的左子樹轉接到p的右子樹上*/ if(r.left != null){ /**如果r的左子樹不為空,將左子樹的父節點設定為p*/ r.left.parent = p; } r.parent = p.parent; /**修改r的父節點,修改為p的父節點*/ if(p.parent == null){ /**如果p的父節點為null,那麼現在r就是根節點了*/ root = r; }else if(p == p.parent.left){/**如果p為其父節點的左孩子,將其父節點的左孩子指向r*/ p.parent.left = r; }else if(p == p.parent.right){/**如果p為其父節點的右孩子,將其父節點的右孩子指向r*/ p.parent.right = r; } r.left = p; /**將r的左孩子設定為p*/ p.parent = r; /**將p的父節點設定為r*/ } } /** * 右旋操作 * * p l * / \ 右旋操作 / \ * l r -------------> ll p * / \ / / \ * ll lr lll lr r * / * lll * */ private void right_Rotate(Node<E> p) { if(p != null){ Node<E> l = p.left; /**獲取p的左孩子l*/ p.left = l.right; /**將l的右子樹變為p的左子樹*/ if(l.right != null){ /**如果l的右子樹不為空,將其父節點設定為p*/ l.right.parent = p; } l.parent = p.parent; /**將r的父節點修改為p的父節點*/ if(p.parent == null){ /**如果p的父節點為null,即l為root*/ root = l; }else if(p == p.parent.left){ /**如果p為其父節點的左孩子,將p的父節點的左孩子指向l*/ p.parent.left = l; }else if(p == p.parent.right){ /**如果p為其父節點的右孩子,將p的父節點的右孩子指向l*/ p.parent.right = l; } l.right = p; /**將l的右子樹變為p*/ p.parent = l; /**修改p的父節點為l*/ } } /**中序非遞迴方式遍歷平衡二叉樹*/ public void nrInOrderTraverse(){ Stack<Node<E>> stack = new Stack<Node<E>>(); Node<E> p = root; while(p != null || !stack.isEmpty()){ while(p != null){ stack.push(p); p = p.left; } p = stack.pop(); System.out.println(p.element); p = p.right; } } /**平衡二叉樹的結點定義*/ class Node<E>{ E element; /**結點的平衡因子*/ int balance = 0; /**左孩子結點、右孩子結點、父節點*/ Node<E> left; Node<E> right; Node<E> parent; public Node(){} public Node(E element, Node<E> parent){ this.element = element; this.parent = parent; } public String toString(){ return element + " BF=" + balance; } } public static void main(String[] args) { Integer[] num = {5,8,2,0,1, -2, -9, 100}; AVLTree<Integer> avl = new AVLTree<Integer>(); for(int i = 0; i < num.length; i++){ avl.insertElement(num[i]); } avl.nrInOrderTraverse(); } }