AVL-平衡二叉樹的原理和實現

1、簡介

  本文將經過圖解和代碼詳細講解AVL平衡二叉樹的性質及失衡和再平衡的內容。在看本文以前但願你們具有二分搜索樹的相關知識。或移步《二分搜索樹》瞭解二分搜索樹。html

 

2、平衡二叉樹

  前面關於二分搜索樹的文章,最後分析了在極端狀況下,二分搜索樹會退化爲一個鏈表,那爲了不這種狀況的發生,AVL平衡二叉樹應運而生。node

  平衡二叉樹的定義:面試

1 平衡二叉樹是一顆二分搜索樹,及平衡二叉樹知足二分搜索樹的全部性質
2 平衡二叉樹要求任意一個節點的左右子樹的高度差不能超過1

  對於第一點應該挺容易理解的,對於第二點,咱們要作一點解釋。對於高度差,有一個專有名詞平衡因子。算法

  平衡因子:左子樹的高度減去右子樹的高度,及B = B左 - B右。由平衡二叉樹的定義可知,平衡因子的取值只可能爲0,1,-1。0:左右子樹等高。1:左子樹比較高。-1:右子樹比較高。以下圖數據結構

  高度:通常的咱們取葉子節點的高度值爲1,任意一個節點的高度值取左右子樹比較高的那個孩子節點的高度值而後加1。好比上圖1中20這個節點的高度值,顯然左子樹比較高,因此H20 = H10 + 1;依次類推,H10 = H6(或者H14) + 1 = 2;因此H20 = 3;測試

  上圖1的樹的各個節點的高度,以下圖1所示。各個節點的平衡因子以下圖2紅色數字所示。因此根據定義,各個節點的左右子樹的高度差不能超過1,及任意一個節點的平衡因子應該爲 -1, 0, 1;因此下面的這棵樹是一棵平衡二叉樹。this

 

3、AVL的失衡及再平衡--旋轉

 

3.一、AVL的失衡狀況

 

  前面咱們介紹了AVL的高度和平衡因子問題,接下來咱們來看看有幾種狀況會致使AVL的失衡,也就是什麼狀況下咱們須要調整AVL樹,使其通過調整後能繼續維持平衡狀態。spa

  如上圖1所示,當咱們已經插入了20,10元素,當咱們再插入6這個元素的時候,很顯然20節點的平衡因子爲2,及B20 = 2 > 1此時該樹已經不平衡了。設計

  如上圖2所示,當咱們已經插入了20,10元素,當咱們再插入14這個元素的時候,很顯然20節點的平衡因子爲2,及B20 = 2 > 1此時該樹已經不平衡了。3d

  如上圖3所示,當咱們已經插入了20,29元素,當咱們再插入33這個元素的時候,很顯然20節點的平衡因子爲-2,及B20 = -2 < -1此時該樹已經不平衡了。

  如上圖4所示,當咱們已經插入了20,29元素,當咱們再插入25這個元素的時候,很顯然20節點的平衡因子爲-2,及B20 = -2 < -1此時該樹已經不平衡了。

  對於AVL,須要進行再平衡操做的狀況正如以上4個圖所示。那接下來咱們須要討論的問題就是如何調整了,及旋轉。

 

3.二、AVL的再平衡--旋轉

  以下圖所示,對於上一章咱們說的四種失衡狀態的調整。而後咱們加下來對照着下面的四張圖片進行逐一介紹旋轉的過程。

  

3.2.一、LL旋轉

  LL,及對於上圖1的待旋轉的樹(中間那棵),形成這棵樹失衡的節點6在20節點的左孩子的左孩子處,left,left簡稱爲LL。這個時候咱們須要進行的旋轉通常稱之爲LL旋轉。對於這種狀況,咱們考慮如何旋轉,始終要考慮如何經過旋轉既達到再平衡的目的,又能維持平衡二叉樹的性質不變,即左孩子 < 父節點 < 右孩子。觀察圖一中插入節點6之後,一個很顯然的結果就是無論咱們怎麼旋轉只有當10節點在中間的時候咱們才能保證這棵樹是平衡的。咱們知道告終果,再看看這個旋轉的過程,爲了保證平衡二叉樹的性質,根據左孩子 < 父節點 < 右孩子的性質,咱們看20 > 10,也就是說,咱們能夠將20節點下移,放到10節點的右孩子處,即獲得圖1中的結果。

  爲了更好地描述這個過程,咱們使用幾個虛擬的節點。

    ///////////////////////////////////////////////////
    // LL T1<Z<T2< X <T3<Y<T4                        //
    //        y                              x       //
    //       / \                           /   \     //
    //      x   T4     向右旋轉 (y)        z     y    //
    //     / \       - - - - - - - ->    / \   / \   //
    //    z   T3                        T1 T2 T3 T4  //
    //   / \                                         //
    // T1   T2                                       //
    ///////////////////////////////////////////////////

 

   如上所示,咱們真實的三個節點爲Y > X > Z。而後咱們爲了方便描述,增長几個虛擬的節點,節點間的大小關係:T1<Z<T2< X <T3<Y<T4

  對於LL,咱們要右旋才能達到再平衡,根據以前描述,咱們須要將Y節點頂替T3的位置,問題來了,T3放哪呢?根據大小關係 X < T3 < Y。咱們能夠將T3放到Y的左孩子節點的位置,這樣進行旋轉後獲得的結果如上所示。咱們發現這棵樹不但達到了再平衡的目的,節點間的大小關係,依然維持了:T1<Z<T2< X <T3<Y<T4的關係。

  代碼實現一下這個過程,先假設咱們的節點爲Node。傳入的參數應該是Y節點

 1     private Node rightRotate(Node y) {
 2         Node x = y.left;
 3         Node T3 = x.right;
 4 
 5         // 向右旋轉過程
 6         x.right = y;
 7         y.left = T3;
 8 
 9         return x;
10     }

 

  對於以上代碼,結合上面咱們分析的過程,你們應該很容易就能理解。

 

3.2.二、RR旋轉

  RR對應上面的圖3,RR及形成AVL失衡的節點6在20節點的右側的右側,即RR。對於RR咱們要進行左旋轉才能實現再平衡。一樣的,咱們若是想經過旋轉達到再平衡,AVL樹的性質依然是咱們實現這個操做的根本。如上圖3所示,若是咱們將20節點移到29元素的左孩子節點處,即可實現再平衡。並且也能維持AVL樹的基本性質。

  同分析LL同樣,咱們增長一些虛擬節點來描述這個過程。

    ////////////////////////////////////////////////
    // RR T1<Y<T2< X <T3<Z<T4                     //
    //    y                             x         //
    //  /  \                          /   \       //
    // T1   x      向左旋轉 (y)       y     z      //
    //     / \   - - - - - - - ->   / \   / \     //
    //    T2  z                    T1 T2 T3 T4    //
    //       / \                                  //
    //      T3 T4                                 //
    ////////////////////////////////////////////////

 

  節點間的大小關係:T1<Y<T2< X <T3<Z<T4。對於RR咱們對Y節點進行左旋轉。即讓Y節點頂替T2,而後根據大小關係:Y < X < T2可知,咱們能夠將T2放到Y的右孩子節點處便可。對Y節點左旋完了如上圖所示的結果。經過比較,節點間的大小關係,依然爲:T1<Y<T2< X <T3<Z<T4。經過對Y節點的左旋轉,達到了AVL的再平衡,並維持了AVL的性質不變。

  代碼實現就不解釋了

 1     private Node leftRotate(Node y) {
 2         Node x = y.right;
 3         Node T2 = x.left;
 4 
 5         // 向左旋轉過程
 6         x.left = y;
 7         y.right = T2;
 8 
 9         return x;
10     }

 

 

3.2.三、LR

  LR對應上圖2,即形成AVL失衡的節點14在節點20的左側的右側,即LR。這種狀況有點複雜,並且有個很想固然的坑,就是將根節點直接換成10不就完事了?但是若是咱們這麼作,發現,10的左節點爲14,不知足:左孩子 < 父節點 < 右孩子的大小關係了。這種狀況呢,正確的作法是先將10節點左旋,而後再將14節點右旋。你們經過以前對LL和RR的分析,在腦子中能不能想象到這個畫面呢?

  爲了方便描述,咱們依然增長一些虛假的節點來描述這個過程。

    //////////////////////////////////////////////////////////////////////////////////////////
    //  LR  T1<X<T2< Z <T3<Y<T4                                                             //
    //         y                                y                              z            //
    //        / \                              / \                           /   \          //
    //       x  t4    向左旋轉(x)             z   T4      向右旋轉(y)      x     y         //
    //      / \     --------------->         / \        --------------->   / \   / \        //
    //     T1  z                            x   T3                        T1  T2 T3 T4      //
    //        / \                          / \                                              //
    //       T2  T3                      T1   T2                                            //
    //////////////////////////////////////////////////////////////////////////////////////////

 

  對於原始的這棵樹呢,大小關係:T1<X<T2< Z <T3<Y<T4。若是咱們先不看Y節點,看X,Z,T3節點,是否是能夠發現,這正是咱們上面描述的RR的狀況啊。對RR,咱們上面已經進行了詳細的分析,經過貴X節點進行左旋,獲得中間那棵樹。這時又一個神奇的事情發生了,這棵樹的形狀又變成了前面咱們說的,LL的狀況。那你們就清楚了,對Y節點進行右旋轉便可。最終的結果如上第三棵樹,達到了AVL的再平衡並依然知足:T1<X<T2< Z <T3<Y<T4。

  咱們發現通過咱們的分析,將這種複雜的狀況進行一步步的拆解即分解成了比較簡單的狀況。不得不感嘆一下:計算機的世界太神奇了。

 

3.2.四、RL

  呃呵,本身看吧,不解釋,不接受反駁。皮一下,很開心。

    //////////////////////////////////////////////////////////////////////////////////////////
    // RL: T1<Y<T2< Z <T3<X<T4                                                              //
    //      y                           y                                       z           //
    //     / \                         / \                                    /   \         //
    //    T1  x       向右旋轉(x)    T1  z         向左旋轉(y)              y     x        //
    //       / \    - - - - - - ->       / \      - - - - - - - - ->        / \   / \       //
    //      z  T4                       T2  x                              T1 T2 T3 T4      //
    //     / \                             / \                                              //
    //    T2  T3                          T3  T4                                            //
    //////////////////////////////////////////////////////////////////////////////////////////

  相信你們看到這裏,被面試官虐千百遍的問題,原來不過如此。其實一切高大上的問題,只要咱們耐心的看下去就能有收穫。

 

3.三、再平衡的時機

 

  首先須要明白,AVL的失衡是因爲節點的變更引發的,也就是增和刪操做纔會致使節點的變更。下面咱們結合平衡因子和插入或者刪除的過程,分析AVL再平衡的時機。

  增長操做再平衡時機:

  對於3.2章節中的過程,但願你們能夠清楚,咱們是經過眼睛觀察來判斷AVL是否是失衡了,可是計算機尚未達到這種能力。因此咱們想一想前面介紹的平衡因子,正是判斷AVL是否是平衡的重要依據。假如如今向一棵空的AVL樹依次插入[20,10,6];三個節點。當插入6節點後,以下圖所示,各個節點的高度。以前說過:B = H左 - H右。咱們看一下20這個節點的平衡因子,B20 = 2 - 0 = 2 > 1;因此,這時20就是不平衡的節點,須要對20這個節點進行旋轉才能再平衡。可是從元素插入操做看一下,很顯然當插入6這個元素的時候,並不知道20這個節點的平衡因子已經不知足要求了。須要沿着添加的元素向上回溯,沿着該節點到根節點的路徑,一步步的從新計算其父節點,爺爺節點,祖父節點...當咱們發現其父節點,爺爺節點...等平衡因子不知足要求的時候,就對該節點進行旋轉。

  刪除操做再平衡的時機:

  刪除操做進行再平衡的時機相似增長操做,須要在刪除節點後沿着其父節點,爺爺節點...一直向上計算各個節點的平衡因子是否知足AVL的性質。當發現某個節點的平衡因子不在[-1, 1]之間的時候,而後判斷其形狀對應的進行左旋轉或者右旋轉使其完成再平衡。

 

4、代碼實現一棵AVL

  

  在前面的章節詳細介紹了AVL的定義,失衡,再平衡即LL,RR,LR,RL等旋轉。接下來咱們經過代碼實現一棵AVL樹。對於咱們要實現的AVL平衡二叉樹,咱們期待具有的功能以下:

1 以Node做爲鏈表的基礎存儲結構
2 使用泛型,並要求該泛型必須實現Comparable接口
3 基本操做:增刪改查

 

  

4.一、AVL的基礎代碼

 1 /**
 2  * 描述:AVL 平衡二叉樹的實現
 3  *
 4  * @Author shf
 5  * @Date 2019/7/31 15:35
 6  * @Version V1.0
 7  **/
 8 public class AVL<K extends Comparable<K>, V> {
 9 
10     private class Node{
11         public K key;
12         public V value;
13         public Node left, right;
14         public int height;// 記錄節點的高度
15 
16         public Node(K key, V value){
17             this.key = key;
18             this.value = value;
19             left = null;
20             right = null;
21             height = 1;
22         }
23     }
24 
25     private Node root;
26     private int size;
27 
28     public AVL(){
29         root = null;
30         size = 0;
31     }
32 
33     public int getSize(){
34         return size;
35     }
36 
37     public boolean isEmpty(){
38         return size == 0;
39     }
40 }

  

  在實現增刪改查以前咱們先設計兩個輔助方法,以下所示,getHeight方法獲取節點的高度值,getBalanceFactor方法獲取節點的平衡因子。

 1     /**
 2      * 得到節點node的高度
 3      * @param node
 4      * @return
 5      */
 6     private int getHeight(Node node){
 7         if(node == null)
 8             return 0;
 9         return node.height;
10     }
11 
12     /**
13      * 得到節點node的平衡因子
14      * @param node
15      * @return
16      */
17     private int getBalanceFactor(Node node){
18         if(node == null)
19             return 0;
20         return getHeight(node.left) - getHeight(node.right);
21     }

 

 

4.二、增

  在《二分搜索樹》介紹了二分搜索樹,前面根據AVL的定義可知,AVL是徹底知足一個二分搜索樹的全部性質的,若是你們想搞明白AVL,仍是建議去先去看一下二分搜索樹。對於二分搜索樹的添加操做的代碼實現,以下所示:

 1     /**
 2      * 添加元素
 3      * @param e
 4      */
 5     public void add(E e){
 6         root = add(root, e);
 7     }
 8 
 9     /**
10      * 添加元素 - 遞歸實現
11      * 時間複雜度 O(log n)
12      * @param node
13      * @param e
14      * @return 返回根節點
15      */
16     public Node add(Node node, E e){
17         if(node == null){// 若是當前節點爲空,則將要添加的節點放到當前節點處
18             size ++;
19             return new Node(e);
20         }
21         if(e.compareTo(node.e) < 0){// 若是小於當前節點,遞歸左孩子
22             node.left = add(node.left, e);
23         } else if(e.compareTo(node.e) > 0){// 若是大於當前節點,遞歸右孩子
24             node.right = add(node.right, e);
25         }
26         return node;
27     }

 

  若是你還無法理解上面的代碼,請移步《二分搜索樹》。對於AVL的添加操做,無非就是在AVL中須要考慮在二分搜索樹失衡的時候,如何經過旋轉達到再平衡。根據咱們前面的介紹,咱們應該已經很明白旋轉的思路了,那咱們直接上代碼吧。

 

 1    /**
 2      * 向以node爲根的二分搜索樹中插入元素(key, value),遞歸算法
 3      * 時間複雜度 O(log n)
 4      * @param node
 5      * @param key
 6      * @param value
 7      * @return 返回插入新節點後二分搜索樹的根
 8      */
 9     private Node add(Node node, K key, V value){
10 
11         if(node == null){
12             size ++;
13             return new Node(key, value);
14         }
15 
16         if(key.compareTo(node.key) < 0)
17             node.left = add(node.left, key, value);
18         else if(key.compareTo(node.key) > 0)
19             node.right = add(node.right, key, value);
20         else // key.compareTo(node.key) == 0
21             node.value = value;
22 
23         // 更新height
24         node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
25 
26         // 計算平衡因子
27         int balanceFactor = getBalanceFactor(node);
28 
29         // 平衡維護
30         //////////////////////////////////////////////////////
31         // LL  T1<Z<T2< X <T3<Y<T4                          //
32         //        y                              x          //
33         //       / \                           /   \        //
34         //      x   T4     向右旋轉 (y)        z     y       //
35         //     / \       - - - - - - - ->    / \   / \      //
36         //    z   T3                        T1 T2 T3 T4     //
37         //   / \                                            //
38         // T1   T2                                          //
39         //////////////////////////////////////////////////////
40         if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0)
41             return rightRotate(node);
42         //////////////////////////////////////////////////////////////////////////////////////////
43         //  LR  T1<X<T2< Z <T3<Y<T4                                                             //
44         //         y                                y                              z            //
45         //        / \                              / \                           /   \          //
46         //       x  t4    向左旋轉(x)             z   T4      向右旋轉(y)       x     y         //
47         //      / \     --------------->         / \        --------------->   / \   / \        //
48         //     T1  z                            x   T3                        T1  T2 T3 T4      //
49         //        / \                          / \                                              //
50         //       T2  T3                      T1   T2                                            //
51         //////////////////////////////////////////////////////////////////////////////////////////
52         if (balanceFactor > 1 && getBalanceFactor(node.left) < 0) {
53             node.left = leftRotate(node.left);
54             return rightRotate(node);
55         }
56         //////////////////////////////////////////////////
57         // RR: T1<Y<T2< X <T3<Z<T4                      //
58         //    y                              x          //
59         //  /  \                           /   \        //
60         // T1   x      向左旋轉 (y)        y     z       //
61         //     / \   - - - - - - - ->    / \   / \      //
62         //   T2   z                     T1 T2 T3 T4     //
63         //       / \                                    //
64         //      T3 T4                                   //
65         //////////////////////////////////////////////////
66         if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0)
67             return leftRotate(node);
68 
69         //////////////////////////////////////////////////////////////////////////////////////////
70         // RL: T1<Y<T2< Z <T3<X<T4                                                              //
71         //      y                           y                                       z           //
72         //     / \                         / \                                    /   \         //
73         //    T1  x       向右旋轉(x)     T1  z         向左旋轉(y)              y     x        //
74         //       / \    - - - - - - ->       / \      - - - - - - - - ->        / \   / \       //
75         //      z  T4                       T2  x                              T1 T2 T3 T4      //
76         //     / \                             / \                                              //
77         //    T2  T3                          T3  T4                                            //
78         //////////////////////////////////////////////////////////////////////////////////////////
79         if (balanceFactor < -1 && getBalanceFactor(node.right) > 0) {
80             node.right = rightRotate(node.right);
81             return leftRotate(node);
82         }
83 
84         return node;
85     }

 

  看上面代碼,輔之旋轉示意圖和平衡因子,相信你們能經過本身的分析,根據當前節點和左右孩子的平衡因子能判斷出來是LL,LR,RR,或者RL的狀況。

 

4.三、左旋和右旋

  至於左右旋轉,咱們前文給出了代碼,但當咱們對節點進行旋轉之後,咱們須要從新維護一下各個節點的高度值。因此通過完善的左右旋轉的代碼以下:

 1     /**
 2      * 對節點y進行向右旋轉操做,返回旋轉後新的根節點x
 3      * @param y
 4      * @return
 5      */
 6     ///////////////////////////////////////////////////
 7     // LL T1<Z<T2< X <T3<Y<T4                        //
 8     //        y                              x       //
 9     //       / \                           /   \     //
10     //      x   T4     向右旋轉 (y)        z     y    //
11     //     / \       - - - - - - - ->    / \   / \   //
12     //    z   T3                        T1 T2 T3 T4  //
13     //   / \                                         //
14     // T1   T2                                       //
15     ///////////////////////////////////////////////////
16     private Node rightRotate(Node y) {
17         Node x = y.left;
18         Node T3 = x.right;
19 
20         // 向右旋轉過程
21         x.right = y;
22         y.left = T3;
23 
24         // 更新height
25         y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
26         x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
27 
28         return x;
29     }
30 
31     /**
32      * 對節點y進行向左旋轉操做,返回旋轉後新的根節點x
33      * @param y
34      * @return
35      */
36     ////////////////////////////////////////////////
37     // RR T1<Y<T2< X <T3<Z<T4                     //
38     //    y                             x         //
39     //  /  \                          /   \       //
40     // T1   x      向左旋轉 (y)       y     z      //
41     //     / \   - - - - - - - ->   / \   / \     //
42     //    T2  z                    T1 T2 T3 T4    //
43     //       / \                                  //
44     //      T3 T4                                 //
45     ////////////////////////////////////////////////
46     private Node leftRotate(Node y) {
47         Node x = y.right;
48         Node T2 = x.left;
49 
50         // 向左旋轉過程
51         x.left = y;
52         y.right = T2;
53 
54         // 更新height
55         y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
56         x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
57 
58         return x;
59     }

  

4.四、刪

  對於刪除,稍微複雜一點,可是基本思路和增長是同樣的。可是關於我以爲仍是有必要給你們惡補一下二分搜索樹的刪除的思路,當咱們刪除一個節點的時候,待刪除節點左右子樹有一個爲空,咱們只須要將其不爲空的子樹的根節點提到待刪除元素的位置便可。若是其左右子樹都不爲空,則將其右子樹最小的元素提到待刪除節點處。詳細的討論請參閱《二分搜索樹》,在二分搜索樹刪除操做的基礎上,咱們只須要輔之再平衡操做便可。

  1     /**
  2      * 從二分搜索樹中刪除鍵爲key的節點
  3      * @param key
  4      * @return
  5      */
  6     public V remove(K key){
  7 
  8         Node node = getNode(root, key);
  9         if(node != null){
 10             root = remove(root, key);
 11             return node.value;
 12         }
 13         return null;
 14     }
 15 
 16     /**
 17      * 刪除指定的節點
 18      * @param node
 19      * @param key
 20      * @return
 21      */
 22     private Node remove(Node node, K key){
 23 
 24         if( node == null )
 25             return null;
 26 
 27         Node retNode;
 28         if( key.compareTo(node.key) < 0 ){
 29             node.left = remove(node.left , key);
 30             // return node;
 31             retNode = node;
 32         }
 33         else if(key.compareTo(node.key) > 0 ){
 34             node.right = remove(node.right, key);
 35             // return node;
 36             retNode = node;
 37         }
 38         else{   // key.compareTo(node.key) == 0 找到待刪除的節點 node
 39 
 40             // 待刪除節點左子樹爲空,直接將右孩子替代當前節點
 41             if(node.left == null){
 42                 Node rightNode = node.right;
 43                 node.right = null;
 44                 size --;
 45                 // return rightNode;
 46                 retNode = rightNode;
 47             }
 48 
 49             // 待刪除節點右子樹爲空,直接將左孩子替代當前節點
 50             else if(node.right == null){
 51                 Node leftNode = node.left;
 52                 node.left = null;
 53                 size --;
 54                 // return leftNode;
 55                 retNode = leftNode;
 56             }
 57 
 58             // 待刪除節點左右子樹均不爲空的狀況
 59             else{
 60                 // 待刪除節點左右子樹均不爲空
 61                 // 找到右子樹最小的元素,替代待刪除節點
 62                 Node successor = minimum(node.right);
 63                 //successor.right = removeMin(node.right);
 64                 successor.right = remove(node.right, successor.key);
 65                 successor.left = node.left;
 66 
 67                 node.left = node.right = null;
 68 
 69                 // return successor;
 70                 retNode = successor;
 71             }
 72         }
 73 
 74         if(retNode == null)
 75             return null;
 76 
 77         // 更新height
 78         retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));
 79 
 80         // 計算平衡因子
 81         int balanceFactor = getBalanceFactor(retNode);
 82 
 83         // 平衡維護
 84         // LL
 85         if (balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)
 86             return rightRotate(retNode);
 87 
 88         // RR
 89         if (balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0)
 90             return leftRotate(retNode);
 91 
 92         // LR
 93         if (balanceFactor > 1 && getBalanceFactor(retNode.left) < 0) {
 94             retNode.left = leftRotate(retNode.left);
 95             return rightRotate(retNode);
 96         }
 97 
 98         // RL
 99         if (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0) {
100             retNode.right = rightRotate(retNode.right);
101             return leftRotate(retNode);
102         }
103 
104         return retNode;
105     }
106     /**
107      * 返回以node爲根的二分搜索樹的最小值所在的節點
108      * @param node
109      * @return
110      */
111     private Node minimum(Node node){
112         if(node.left == null)
113             return node;
114         return minimum(node.left);
115     }

  

 4.五、其餘操做

 1     /**
 2      * 返回以node爲根節點的二分搜索樹中,key所在的節點
 3      * @param node
 4      * @param key
 5      * @return
 6      */
 7     private Node getNode(Node node, K key){
 8 
 9         if(node == null)
10             return null;
11 
12         if(key.equals(node.key))
13             return node;
14         else if(key.compareTo(node.key) < 0)
15             return getNode(node.left, key);
16         else // if(key.compareTo(node.key) > 0)
17             return getNode(node.right, key);
18     }
19 
20     /**
21      * 判斷是否包含 key
22      * @param key
23      * @return
24      */
25     public boolean contains(K key){
26         return getNode(root, key) != null;
27     }
28 
29     /**
30      * 獲取指定 key 的 value
31      * @param key
32      * @return
33      */
34     public V get(K key){
35 
36         Node node = getNode(root, key);
37         return node == null ? null : node.value;
38     }
39 
40     /**
41      * 設置 key 對應元素的值 value
42      * @param key
43      * @param newValue
44      */
45     public void set(K key, V newValue){
46         Node node = getNode(root, key);
47         if(node == null)
48             throw new IllegalArgumentException(key + " doesn't exist!");
49 
50         node.value = newValue;
51     }

 

 

 5、驗證AVL

  前面咱們實現了一棵AVL樹,咱們如何驗證這究竟是不是一棵AVL樹呢?這個問題,咱們依然是從其定義來思考,首先,AVL是一棵二分搜索樹,其次每一個節點的平衡因子能知足在[-1, 1]之間,能知足這兩點其實加之咱們代碼的邏輯便可判斷其是否是一棵AVL樹了。

  二分搜索樹有一個延伸出來的性質不知道你們還記不記得,對於二分搜索樹的中序遍歷,實際上是對二分搜索樹從小到大排序的過程。那咱們判斷中序遍歷的結果滿不知足從小到大便可斷定其是否是一棵二分搜索樹。

 1     /**
 2      * 測試方法 - 判斷該二叉樹是不是一棵二分搜索樹
 3      * @return
 4      */
 5     public boolean isBST(){
 6 
 7         ArrayList<K> keys = new ArrayList<>();
 8         inOrder(root, keys);
 9         for(int i = 1 ; i < keys.size() ; i ++)
10             if(keys.get(i - 1).compareTo(keys.get(i)) > 0)
11                 return false;
12         return true;
13     }
14 
15     /**
16      * 中序遍歷
17      * @param node
18      * @param keys
19      */
20     private void inOrder(Node node, ArrayList<K> keys){
21 
22         if(node == null)
23             return;
24 
25         inOrder(node.left, keys);
26         keys.add(node.key);
27         inOrder(node.right, keys);
28     }
29 
30     /**
31      * 測試方法 - 判斷該二叉樹是不是一棵平衡二叉樹
32      * @return
33      */
34     public boolean isBalanced(){
35         return isBalanced(root);
36     }
37 
38     /**
39      * 判斷以Node爲根的二叉樹是不是一棵平衡二叉樹,遞歸算法
40      * @param node
41      * @return
42      */
43     private boolean isBalanced(Node node){
44 
45         if(node == null)
46             return true;
47 
48         int balanceFactor = getBalanceFactor(node);
49         if(Math.abs(balanceFactor) > 1)
50             return false;
51         return isBalanced(node.left) && isBalanced(node.right);
52     }

 

 

  咱們寫以下測試代碼:

public class Main {
    public static void main(String[] args) {
        AVL<Integer, Integer> avl = new AVL<>();
        for (int i=0; i< 10; i++){
            avl.add(i, i);
        }
        System.out.println(avl.isBST());
        System.out.println(avl.isBalanced());
        avl.remove(5);
        System.out.println(avl.isBST());
        System.out.println(avl.isBalanced());
    }
}
true
true
true
true

 

 

   到此,AVL全部的內容咱們已經介紹完了。哎呦,凌晨三點了,心疼本身一秒鐘。我愛個人國。

 

 

 

 

  參考文獻:

  《玩轉數據結構-從入門到進階-劉宇波》

  《數據結構與算法分析-Java語言描述》

 

 

  若有錯誤的地方還請留言指正。

  原創不易,轉載請註明原文地址:https://www.cnblogs.com/hello-shf/p/11352071.html 

相關文章
相關標籤/搜索