在計算機科學中,AVL樹是最先發明的自平衡二叉查找樹。AVL樹得名於它的發明者 G.M. Adelson-Velsky 和 E.M. Landis,他們在 1962 年的論文 "An algorithm for the organization of information" 中發表了它。
一、AVL樹的旋轉規律
AVL樹的基本操作一般涉及運做同在不平衡的二叉查找樹所運做的同樣的算法。但是要進行預先或隨後做一次或多次所謂的"AVL旋轉"。
假設由於在二叉排序樹上插入結點而失去平衡的最小子樹根結點的指針爲a(即a是離插入點最近,且平衡因子絕對值超過1的祖先結點),則失去平衡後進行進行的規律可歸納爲下列四種情況:
1. LL型
平衡二叉樹某一節點的左孩子的左子樹上插入一個新的節點,使得該節點不再平衡。這時只需要把樹向右旋轉一次即可,如圖所示,原A的左孩子B變爲父結點,A變爲其右孩子,而原B的右子樹變爲A的左子樹,注意旋轉之後Brh是A的左子樹(圖上忘在A於Brh之間標實線)
2. RR型
平衡二叉樹某一節點的右孩子的右子樹上插入一個新的節點,使得該節點不再平衡。這時只需要把樹向左旋轉一次即可,如圖所示,原A右孩子B變爲父結點,A變爲其左孩子,而原B的左子樹Blh將變爲A的右子樹。
3. LR型
平衡二叉樹某一節點的左孩子的右子樹上插入一個新的節點,使得該節點不再平衡。這時需要旋轉兩次,僅一次的旋轉是不能夠使二叉樹再次平衡。如圖所示,在B節點按照RR型向左旋轉一次之後,二叉樹在A節點仍然不能保持平衡,這時還需要再向右旋轉一次。
4. RL型
平衡二叉樹某一節點的右孩子的左子樹上插入一個新的節點,使得該節點不再平衡。同樣,這時需要旋轉兩次,旋轉方向剛好同LR型相反。
二、AVL樹的基本操作
1.插入
向AVL樹插入可以通過如同它是未平衡的二叉查找樹一樣把給定的值插入樹中,接着自底向上向根節點折回,於在插入期間成爲不平衡的所有節點上進行旋轉來完成。因爲折回到根節點的路途上最多有 1.5 乘 log n 個節點,而每次AVL 旋轉都耗費恆定的時間,插入處理在整體上耗費 O(log n) 時間。
在平衡的的二叉排序樹Balanced BST上插入一個新的數據元素e的遞歸算法可描述如下:
若BBST爲空樹,則插入一個數據元素爲e的新結點作爲BBST的根結點,樹的深度增1;
若e的關鍵字和BBST的根結點的關鍵字相等,則不進行;
若e的關鍵字小於BBST的根結點的關鍵字,而且在BBST的左子樹中不存在和e有相同關鍵字的結點,則將e插入在BBST的左子樹上,並且當插入之後的左子樹深度增加(+1)時,分別就下列不同情況處理之:BBST的根結點的平衡因子爲-1(右子樹的深度大於左子樹的深度,則將根結點的平衡因子更改爲 0,BBST的深度不變; BBST的根結點的平衡因子爲0(左、右子樹的深度相等):則將根結點的平衡因子更改爲1,BBST的深度增1;BBST的根結點的平衡因子爲1(左子樹的深度大於右子樹的深度):則若BBST的左子樹根結點的平衡因子爲1:則需進行單向右旋平衡處理,並且在右旋處理之後,將根結點和其右子樹根結點的平衡因子更改爲0,樹的深度不變;若e的關鍵字大於BBST的根結點的關鍵字,而且在BBST的右子樹中不存在和e有相同關鍵字的結點,則將e插入在BBST的右子樹上,並且當插入之後的 右子樹深度增加(+1)時,分別就不同情況處理之。
2.刪除
從AVL樹中刪除可以通過把要刪除的節點向下旋轉成一個葉子節點,接着直接剪除這個葉子節點來完成。因爲在旋轉成葉子節點期間最多有 log n個節點被旋轉,而每次 AVL 旋轉耗費恆定的時間,刪除處理在整體上耗費 O(log n) 時間。
刪除操作需要考慮的情況較多,具體見代碼實現吧。
3.查找
在AVL樹中查找同在一般BST完全一樣的進行,所以耗費 O(log n) 時間,因爲AVL樹總是保持平衡的。不需要特殊的準備,樹的結構不會由於查詢而改變。(這是與伸展樹查找相對立的,它會因爲查找而變更樹結構。)
三、代碼實現
時間倉促,對於插入、刪除操作沒有就各種情況配上插圖,代碼裏面有一些註釋,可以對着代碼理解。日後再研究這個的時候定配上插圖。
- package ly.dataStructures.tree;
- import java.util.Comparator;
- /**
- * AVL樹
- * @author 無間道風雲
- * 2014.0526
- * @param <AnyType>
- */
- public class AvlTree<AnyType extends Comparable<? super AnyType>> {
- private AvlNode<AnyType> root;
- private Comparator<? super AnyType> cmp;
- /********* AVL樹節點數據結構定義 **********/
- private static class AvlNode<AnyType>{
- AnyType element;
- AvlNode<AnyType> left;
- AvlNode<AnyType> right;
- int height;
- AvlNode(AnyType theElement){
- this(theElement, null, null);
- }
- AvlNode(AnyType theElement, AvlNode<AnyType> lt, AvlNode<AnyType> rt){
- element = theElement;
- left = lt;
- right = rt;
- height = 0;
- }
- }
- public AvlTree(){
- root = null;
- }
- public void makeEmpty(){
- root = null;
- }
- public boolean isEmpty(){
- return root == null;
- }
- public void insert(AnyType element){
- root = insert(element, root);
- }
- public boolean contains(AnyType x){
- return contains(x, root);
- }
- public void remove(AnyType element){
- root = remove(element, root);
- }
- private int myCompare(AnyType lhs, AnyType rhs){
- if(cmp != null)
- return cmp.compare(lhs, rhs);
- else
- return ((Comparable)lhs).compareTo(rhs);
- }
- private boolean contains(AnyType x, AvlNode<AnyType> t){
- //空樹處理
- if(t == null)
- return false;
- //正常情況處理
- //@方式一:對Comparable型的對象進行比較
- //int compareResult = x.compareTo(t.element);
- //@方式二:使用一個函數對象而不是要求這些項是Comparable的
- int compareResult = myCompare(x, t.element);
- if(compareResult < 0)
- return contains(x, t.left);
- else if(compareResult > 0)
- return contains(x, t.right);
- else
- return true;
- }
- private int height(AvlNode<AnyType> t){
- return t == null ? -1 : t.height;
- }
- private AvlNode<AnyType> findMin(AvlNode<AnyType> t){
- if(t == null)
- return null;
- if(t.left == null)
- return t;
- return findMin(t.left);
- }
- private AvlNode<AnyType> findMax(AvlNode<AnyType> t){
- if(t == null)
- return null;
- if(t.right == null)
- return t;
- return findMax(t.right);
- }
- private AvlNode<AnyType> insert(AnyType x, AvlNode<AnyType> t){
- if(t == null)
- return new AvlNode<AnyType>(x, null, null);
- int compareResult = myCompare(x, t.element);
- if(compareResult < 0){
- t.left = insert(x, t.left);
- if(height(t.left)-height(t.right) == 2){
- if(myCompare(x, t.left.element) < 0) //左左情況
- t = rotateWithLeftChild(t);
- else //左右情況
- t = doubleWithLeftChild(t);
- }
- }else if(compareResult > 0){
- t.right = insert(x, t.right);
- if(height(t.right)-height(t.left) == 2){
- if(myCompare(x, t.right.element) < 0) //右左情況
- t = doubleWithRightChild(t);
- else //右右情況
- t = rotateWithRightChild(t);
- }
- }
- //完了之後更新height值
- t.height = Math.max(height(t.left), height(t.right))+1;
- return t;
- }
- private AvlNode<AnyType> remove(AnyType x, AvlNode<AnyType> t){
- if(t == null)
- return null;
- int compareResult = myCompare(x, t.element);
- if(compareResult < 0){
- t.left = remove(x, t.left);
- //完了之後驗證該子樹是否平衡
- if(t.right != null){ //若右子樹爲空,則一定是平衡的,此時左子樹相當對父節點深度最多爲1, 所以只考慮右子樹非空情況
- if(t.left == null){ //若左子樹刪除後爲空,則需要判斷右子樹
- if(height(t.right)-t.height == 2){
- AvlNode<AnyType> k = t.right;
- if(k.right != null){ //右子樹存在,按正常情況單旋轉
- System.out.println("-----------------------------------------------------------------------------11111");
- t = rotateWithRightChild(t);
- }else{ //否則是右左情況,雙旋轉
- System.out.println("-----------------------------------------------------------------------------22222");
- t = doubleWithRightChild(t);
- }
- }
- }else{ //否則判斷左右子樹的高度差
- //左子樹自身也可能不平衡,故先平衡左子樹,再考慮整體
- AvlNode<AnyType> k = t.left;
- //刪除操作默認用右子樹上最小節點補刪除的節點
- //k的左子樹高度不低於k的右子樹
- if(k.right != null){
- if(height(k.left)-height(k.right) == 2){
- AvlNode<AnyType> m = k.left;
- if(m.left != null){ //左子樹存在,按正常情況單旋轉
- System.out.println("-----------------------------------------------------------------------------33333");
- k = rotateWithLeftChild(k);
- }else{ //否則是左右情況,雙旋轉
- System.out.println("-----------------------------------------------------------------------------44444");
- k = doubleWithLeftChild(k);
- }
- }
- }else{
- if(height(k.left) - k.height ==2){
- AvlNode<AnyType> m = k.left;
- if(m.left != null){ //左子樹存在,按正常情況單旋轉
- System.out.println("-----------------------------------------------------------------------------hhhhh");
- k = rotateWithLeftChild(k);
- }else{ //否則是左右情況,雙旋轉
- System.out.println("-----------------------------------------------------------------------------iiiii");
- k = doubleWithLeftChild(k);
- }
- }
- }
- if(height(t.right)-height(t.left) == 2){
- //右子樹自身一定是平衡的,左右失衡的話單旋轉可以解決問題
- System.out.println("-----------------------------------------------------------------------------55555");
- t = rotateWithRightChild(t);
- }
- }
- }
- //完了之後更新height值
- t.height = Math.max(height(t.left), height(t.right))+1;
- }else if(compareResult > 0){
- t.right = remove(x, t.right);
- //下面驗證子樹是否平衡
- if(t.left != null){ //若左子樹爲空,則一定是平衡的,此時右子樹相當對父節點深度最多爲1
- if(t.right == null){ //若右子樹刪除後爲空,則只需判斷左子樹
- if(height(t.left)-t.height ==2){
- AvlNode<AnyType> k = t.left;
- if(k.left != null){
- System.out.println("-----------------------------------------------------------------------------66666");
- t = rotateWithLeftChild(t);
- }else{
- System.out.println("-----------------------------------------------------------------------------77777");
- t = doubleWithLeftChild(t);
- }
- }
- }else{ //若右子樹刪除後非空,則判斷左右子樹的高度差
- //右子樹自身也可能不平衡,故先平衡右子樹,再考慮整體
- AvlNode<AnyType> k = t.right;
- //刪除操作默認用右子樹上最小節點(靠左)補刪除的節點
- //k的右子樹高度不低於k的左子樹
- if(k.left != null){
- if(height(k.right)-height(k.left) == 2){
- AvlNode<AnyType> m = k.right;
- if(m.right != null){ //右子樹存在,按正常情況單旋轉
- System.out.println("-----------------------------------------------------------------------------88888");
- k = rotateWithRightChild(k);
- }else{ //否則是右左情況,雙旋轉
- System.out.println("-----------------------------------------------------------------------------99999");
- k = doubleWithRightChild(k);
- }
- }
- }else{
- if(height(k.right)-k.height == 2){
- AvlNode<AnyType> m = k.right;
- if(m.right != null){ //右子樹存在,按正常情況單旋轉
- System.out.println("-----------------------------------------------------------------------------aaaaa");
- k = rotateWithRightChild(k);
- }else{ //否則是右左情況,雙旋轉
- System.out.println("-----------------------------------------------------------------------------bbbbb");
- k = doubleWithRightChild(k);
- }
- }
- }
- if(height(t.left) - height(t.right) == 2){
- //左子樹自身一定是平衡的,左右失衡的話單旋轉可以解決問題
- System.out.println("-----------------------------------------------------------------------------ccccc");
- t = rotateWithLeftChild(t);
- }
- }
- }
- //完了之後更新height值
- t.height = Math.max(height(t.left), height(t.right))+1;
- }else if(t.left != null && t.right != null){
- //默認用其右子樹的最小數據代替該節點的數據並遞歸的刪除那個節點
- t.element = findMin(t.right).element;
- t.right = remove(t.element, t.right);
- if(t.right == null){ //若右子樹刪除後爲空,則只需判斷左子樹與根的高度差
- if(height(t.left)-t.height ==2){
- AvlNode<AnyType> k = t.left;
- if(k.left != null){
- System.out.println("-----------------------------------------------------------------------------ddddd");
- t = rotateWithLeftChild(t);
- }else{
- System.out.println("-----------------------------------------------------------------------------eeeee");
- t = doubleWithLeftChild(t);
- }
- }
- }else{ //若右子樹刪除後非空,則判斷左右子樹的高度差
- //右子樹自身也可能不平衡,故先平衡右子樹,再考慮整體
- AvlNode<AnyType> k = t.right;
- //刪除操作默認用右子樹上最小節點(靠左)補刪除的節點
- if(k.left != null){
- if(height(k.right)-height(k.left) == 2){
- AvlNode<AnyType> m = k.right;
- if(m.right != null){ //右子樹存在,按正常情況單旋轉
- System.out.println("-----------------------------------------------------------------------------fffff");
- k = rotateWithRightChild(k);
- }else{ //否則是右左情況,雙旋轉
- System.out.println("-----------------------------------------------------------------------------ggggg");
- k = doubleWithRightChild(k);
- }
- }
- }else{
- if(height(k.right)-k.height == 2){
- AvlNode<AnyType> m = k.right;
- if(m.right != null){ //右子樹存在,按正常情況單旋轉
- System.out.println("-----------------------------------------------------------------------------hhhhh");
- k = rotateWithRightChild(k);
- }else{ //否則是右左情況,雙旋轉
- System.out.println("-----------------------------------------------------------------------------iiiii");
- k = doubleWithRightChild(k);
- }
- }
- }
- //左子樹自身一定是平衡的,左右失衡的話單旋轉可以解決問題
- if(height(t.left) - height(t.right) == 2){
- System.out.println("-----------------------------------------------------------------------------jjjjj");
- t = rotateWithLeftChild(t);
- }
- }
- //完了之後更新height值
- t.height = Math.max(height(t.left), height(t.right))+1;
- }else{
- System.out.println("-----------------------------------------------------------------------------kkkkk");
- t = (t.left != null)?t.left:t.right;
- }
- return t;
- }
- //左左情況單旋轉
- private AvlNode<AnyType> rotateWithLeftChild(AvlNode<AnyType> k2){
- AvlNode<AnyType> k1 = k2.left;
- k2.left = k1.right;
- k1.right = k2;
- k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
- k1.height = Math.max(height(k1.left), k2.height) + 1;
- return k1; //返回新的根
- }
- //右右情況單旋轉
- private AvlNode<AnyType> rotateWithRightChild(AvlNode<AnyType> k2){
- AvlNode<AnyType> k1 = k2.right;
- k2.right = k1.left;
- k1.left = k2;
- k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
- k1.height = Math.max(height(k1.right), k2.height) + 1;
- return k1; //返回新的根
- }
- //左右情況
- private AvlNode<AnyType> doubleWithLeftChild(AvlNode<AnyType> k3){
- try{
- k3.left = rotateWithRightChild(k3.left);
- }catch(NullPointerException e){
- System.out.println("k.left.right爲:"+k3.left.right);
- throw e;
- }
- return rotateWithLeftChild(k3);
- }
- //右左情況
- private AvlNode<AnyType> doubleWithRightChild(AvlNode<AnyType> k3){
- try{
- k3.right = rotateWithLeftChild(k3.right);
- }catch(NullPointerException e){
- System.out.println("k.right.left爲:"+k3.right.left);
- throw e;
- }
- return rotateWithRightChild(k3);
- }
- }
- /*註明:由於刪除操作考慮的情況甚多,代碼中出現的打印信息主要爲方便排錯*/
- import static org.junit.Assert.*;
- import java.util.Random;
- import org.junit.Test;
- public class AvlTreeTest {
- private AvlTree<Integer> avlTree = new AvlTree<Integer>();
- @Test
- public void testInsert(){
- avlTree.insert(100);
- avlTree.insert(120);
- avlTree.insert(300);
- avlTree.insert(500);
- avlTree.insert(111);
- avlTree.insert(92);
- avlTree.insert(77);
- avlTree.insert(125);
- System.out.println(avlTree.contains(120));
- avlTree.remove(120);
- avlTree.remove(125); //需要單旋轉
- System.out.println(avlTree.contains(120));
- avlTree.insert(78); //需要雙旋轉
- System.out.println("Insert Success !");
- }
- @Test
- public void testRotate(){
- avlTree.insert(100);
- avlTree.insert(90);
- avlTree.insert(92);
- avlTree.insert(78);
- avlTree.insert(76);
- System.out.println("Insert Success !");
- }
- /**
- * 通過較大數據進行測試,暫時還沒有發現問題
- */
- @Test
- public void testAll(){
- avlTree.makeEmpty();
- Random random = new Random();
- for(int i=1;i<=1000000;i++){
- avlTree.insert(random.nextInt(1000000));
- }
- for(int i=2000000;i>=1000000;i--){
- avlTree.insert(i);
- }
- /*for(int i=700000;i>=400000;i--){
- avlTree.insert(i);
- }
- for(int i=100000;i<=200000;i++){
- avlTree.insert(i);
- }
- for(int i=400000;i<=500000;i++){
- avlTree.insert(random.nextInt(600000));
- }*/
- for(int i=200000;i<1400000;i++){
- int target = random.nextInt(1500000);
- if(avlTree.contains(target)){
- avlTree.remove(target);
- }
- }
- System.out.println("Insert Success !");
- }
- }