AVL樹—java

[本文轉載至](http://www.cnblogs.com/skywang12345/p/3577479.html)

 

AVL樹的介紹

AVL樹是高度平衡的而二叉樹。它的特點是:AVL樹中任何節點的兩個子樹的高度最大差別爲1。 

上面的兩張圖片,左邊的是AVL樹,它的任何節點的兩個子樹的高度差別都<=1;而右邊的不是AVL樹,因爲7的兩顆子樹的高度相差爲2(以2爲根節點的樹的高度是3,而以8爲根節點的樹的高度是1)。

 

AVL樹的Java實現

1. 節點

1.1 節點定義

複製代碼
public class AVLTree<T extends Comparable<T>> {
    private AVLTreeNode<T> mRoot;    // 根結點

    // AVL樹的節點(內部類)
    class AVLTreeNode<T extends Comparable<T>> {
        T key;                // 關鍵字(鍵值)
        int height;         // 高度
        AVLTreeNode<T> left;    // 左孩子
        AVLTreeNode<T> right;    // 右孩子

        public AVLTreeNode(T key, AVLTreeNode<T> left, AVLTreeNode<T> right) {
            this.key = key;
            this.left = left;
            this.right = right;
            this.height = 0;
        }
    }

    ......
}
複製代碼

AVLTree是AVL樹對應的類,而AVLTreeNode是AVL樹節點,它是AVLTree的內部類。AVLTree包含了AVL樹的根節點,AVL樹的基本操作也定義在AVL樹中。AVLTreeNode包括的幾個組成對象:
(01) key – 是關鍵字,是用來對AVL樹的節點進行排序的。
(02) left – 是左孩子。
(03) right – 是右孩子。
(04) height – 是高度。

 

1.2 樹的高度

複製代碼
/*
 * 獲取樹的高度
 */
private int height(AVLTreeNode<T> tree) {
    if (tree != null)
        return tree.height;

    return 0;
}

public int height() {
    return height(mRoot);
}
複製代碼

關於高度,有的地方將”空二叉樹的高度是-1”,而本文采用維基百科上的定義:樹的高度爲最大層次。即空的二叉樹的高度是0,非空樹的高度等於它的最大層次(根的層次爲1,根的子節點爲第2層,依次類推)。

 

1.3 比較大小

/*
 * 比較兩個值的大小
 */
private int max(int a, int b) {
    return a>b ? a : b;
}

 

2. 旋轉

如果在AVL樹中進行插入或刪除節點後,可能導致AVL樹失去平衡。這種失去平衡的可以概括爲4種姿態:LL(左左),LR(左右),RR(右右)和RL(右左)。下面給出它們的示意圖:


上圖中的4棵樹都是”失去平衡的AVL樹”,從左往右的情況依次是:LL、LR、RL、RR。除了上面的情況之外,還有其它的失去平衡的AVL樹,如下圖:


上面的兩張圖都是爲了便於理解,而列舉的關於”失去平衡的AVL樹”的例子。總的來說,AVL樹失去平衡時的情況一定是LL、LR、RL、RR這4種之一,它們都由各自的定義:

(1) LL:LeftLeft,也稱爲”左左”。插入或刪除一個節點後,根節點的左子樹的左子樹還有非空子節點,導致”根的左子樹的高度”比”根的右子樹的高度”大2,導致AVL樹失去了平衡。
     例如,在上面LL情況中,由於”根節點(8)的左子樹(4)的左子樹(2)還有非空子節點”,而”根節點(8)的右子樹(12)沒有子節點”;導致”根節點(8)的左子樹(4)高度”比”根節點(8)的右子樹(12)”高2。

 

(2) LR:LeftRight,也稱爲”左右”。插入或刪除一個節點後,根節點的左子樹的右子樹還有非空子節點,導致”根的左子樹的高度”比”根的右子樹的高度”大2,導致AVL樹失去了平衡。
     例如,在上面LR情況中,由於”根節點(8)的左子樹(4)的左子樹(6)還有非空子節點”,而”根節點(8)的右子樹(12)沒有子節點”;導致”根節點(8)的左子樹(4)高度”比”根節點(8)的右子樹(12)”高2。

 

(3) RL:RightLeft,稱爲”右左”。插入或刪除一個節點後,根節點的右子樹的左子樹還有非空子節點,導致”根的右子樹的高度”比”根的左子樹的高度”大2,導致AVL樹失去了平衡。
     例如,在上面RL情況中,由於”根節點(8)的右子樹(12)的左子樹(10)還有非空子節點”,而”根節點(8)的左子樹(4)沒有子節點”;導致”根節點(8)的右子樹(12)高度”比”根節點(8)的左子樹(4)”高2。

 

(4) RR:RightRight,稱爲”右右”。插入或刪除一個節點後,根節點的右子樹的右子樹還有非空子節點,導致”根的右子樹的高度”比”根的左子樹的高度”大2,導致AVL樹失去了平衡。
     例如,在上面RR情況中,由於”根節點(8)的右子樹(12)的右子樹(14)還有非空子節點”,而”根節點(8)的左子樹(4)沒有子節點”;導致”根節點(8)的右子樹(12)高度”比”根節點(8)的左子樹(4)”高2。


如果在AVL樹中進行插入或刪除節點後,可能導致AVL樹失去平衡。AVL失去平衡之後,可以通過旋轉使其恢復平衡,下面分別介紹”LL(左左),LR(左右),RR(右右)和RL(右左)”這4種情況對應的旋轉方法。

 

2.1 LL的旋轉

LL失去平衡的情況,可以通過一次旋轉讓AVL樹恢復平衡。如下圖:


圖中左邊是旋轉之前的樹,右邊是旋轉之後的樹。從中可以發現,旋轉之後的樹又變成了AVL樹,而且該旋轉只需要一次即可完成。
對於LL旋轉,你可以這樣理解爲:LL旋轉是圍繞”失去平衡的AVL根節點”進行的,也就是節點k2;而且由於是LL情況,即左左情況,就用手抓着”左孩子,即k1”使勁搖。將k1變成根節點,k2變成k1的右子樹,”k1的右子樹”變成”k2的左子樹”。

 

LL的旋轉代碼

複製代碼
/*
 * LL:左左對應的情況(左單旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
private AVLTreeNode<T> leftLeftRotation(AVLTreeNode<T> k2) {
    AVLTreeNode<T> k1;

    k1 = k2.left;
    k2.left = k1.right;
    k1.right = k2;

    k2.height = max( height(k2.left), height(k2.right)) + 1;
    k1.height = max( height(k1.left), k2.height) + 1;

    return k1;
}
複製代碼

 

2.2 RR的旋轉

理解了LL之後,RR就相當容易理解了。RR是與LL對稱的情況!RR恢復平衡的旋轉方法如下:

圖中左邊是旋轉之前的樹,右邊是旋轉之後的樹。RR旋轉也只需要一次即可完成。

RR的旋轉代碼

複製代碼
/*
 * RR:右右對應的情況(右單旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
private AVLTreeNode<T> rightRightRotation(AVLTreeNode<T> k1) {
    AVLTreeNode<T> k2;

    k2 = k1.right;
    k1.right = k2.left;
    k2.left = k1;

    k1.height = max( height(k1.left), height(k1.right)) + 1;
    k2.height = max( height(k2.right), k1.height) + 1;

    return k2;
}
複製代碼

 

2.3 LR的旋轉

LR失去平衡的情況,需要經過兩次旋轉才能讓AVL樹恢復平衡。如下圖:


第一次旋轉是圍繞”k1”進行的”RR旋轉”,第二次是圍繞”k3”進行的”LL旋轉”。

 

LR的旋轉代碼

複製代碼
/*
 * LR:左右對應的情況(左雙旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
private AVLTreeNode<T> leftRightRotation(AVLTreeNode<T> k3) {
    k3.left = rightRightRotation(k3.left);

    return leftLeftRotation(k3);
}
複製代碼

 

2.4 RL的旋轉

RL是與LR的對稱情況!RL恢復平衡的旋轉方法如下:

第一次旋轉是圍繞”k3”進行的”LL旋轉”,第二次是圍繞”k1”進行的”RR旋轉”。

 

RL的旋轉代碼

複製代碼
/*
 * RL:右左對應的情況(右雙旋轉)。
 *
 * 返回值:旋轉後的根節點
 */
private AVLTreeNode<T> rightLeftRotation(AVLTreeNode<T> k1) {
    k1.right = leftLeftRotation(k1.right);

    return rightRightRotation(k1);
}
複製代碼

 

3. 插入

插入節點的代碼

複製代碼
/* 
 * 將結點插入到AVL樹中,並返回根節點
 *
 * 參數說明:
 *     tree AVL樹的根結點
 *     key 插入的結點的鍵值
 * 返回值:
 *     根節點
 */
private AVLTreeNode<T> insert(AVLTreeNode<T> tree, T key) {
    if (tree == null) {
        // 新建節點
        tree = new AVLTreeNode<T>(key, null, null);
        if (tree==null) {
            System.out.println("ERROR: create avltree node failed!");
            return null;
        }
    } else {
        int cmp = key.compareTo(tree.key);

           if (cmp < 0) {    // 應該將key插入到"tree的左子樹"的情況
            tree.left = insert(tree.left, key);
            // 插入節點後,若AVL樹失去平衡,則進行相應的調節。
            if (height(tree.left) - height(tree.right) == 2) {
                if (key.compareTo(tree.left.key) < 0)
                    tree = leftLeftRotation(tree);
                else
                    tree = leftRightRotation(tree);
            }
        } else if (cmp > 0) {    // 應該將key插入到"tree的右子樹"的情況
            tree.right = insert(tree.right, key);
            // 插入節點後,若AVL樹失去平衡,則進行相應的調節。
            if (height(tree.right) - height(tree.left) == 2) {
                if (key.compareTo(tree.right.key) > 0)
                    tree = rightRightRotation(tree);
                else
                    tree = rightLeftRotation(tree);
            }
        } else {    // cmp==0
            System.out.println("添加失敗:不允許添加相同的節點!");
        }
    }

    tree.height = max( height(tree.left), height(tree.right)) + 1;

    return tree;
}

public void insert(T key) {
    mRoot = insert(mRoot, key);
}
複製代碼

 

4. 刪除

刪除節點的代碼

複製代碼
/* 
 * 刪除結點(z),返回根節點
 *
 * 參數說明:
 *     tree AVL樹的根結點
 *     z 待刪除的結點
 * 返回值:
 *     根節點
 */
private AVLTreeNode<T> remove(AVLTreeNode<T> tree, AVLTreeNode<T> z) {
    // 根爲空 或者 沒有要刪除的節點,直接返回null。
    if (tree==null || z==null)
        return null;

    int cmp = z.key.compareTo(tree.key);
    if (cmp < 0) {        // 待刪除的節點在"tree的左子樹"中
        tree.left = remove(tree.left, z);
        // 刪除節點後,若AVL樹失去平衡,則進行相應的調節。
        if (height(tree.right) - height(tree.left) == 2) {
            AVLTreeNode<T> r =  tree.right;
            if (height(r.left) > height(r.right))
                tree = rightLeftRotation(tree);
            else
                tree = rightRightRotation(tree);
        }
    } else if (cmp > 0) {    // 待刪除的節點在"tree的右子樹"中
        tree.right = remove(tree.right, z);
        // 刪除節點後,若AVL樹失去平衡,則進行相應的調節。
        if (height(tree.left) - height(tree.right) == 2) {
            AVLTreeNode<T> l =  tree.left;
            if (height(l.right) > height(l.left))
                tree = leftRightRotation(tree);
            else
                tree = leftLeftRotation(tree);
        }
    } else {    // tree是對應要刪除的節點。
        // tree的左右孩子都非空
        if ((tree.left!=null) && (tree.right!=null)) {
            if (height(tree.left) > height(tree.right)) {
                // 如果tree的左子樹比右子樹高;
                // 則(01)找出tree的左子樹中的最大節點
                //   (02)將該最大節點的值賦值給tree。
                //   (03)刪除該最大節點。
                // 這類似於用"tree的左子樹中最大節點"做"tree"的替身;
                // 採用這種方式的好處是:刪除"tree的左子樹中最大節點"之後,AVL樹仍然是平衡的。
                AVLTreeNode<T> max = maximum(tree.left);
                tree.key = max.key;
                tree.left = remove(tree.left, max);
            } else {
                // 如果tree的左子樹不比右子樹高(即它們相等,或右子樹比左子樹高1)
                // 則(01)找出tree的右子樹中的最小節點
                //   (02)將該最小節點的值賦值給tree。
                //   (03)刪除該最小節點。
                // 這類似於用"tree的右子樹中最小節點"做"tree"的替身;
                // 採用這種方式的好處是:刪除"tree的右子樹中最小節點"之後,AVL樹仍然是平衡的。
                AVLTreeNode<T> min = maximum(tree.right);
                tree.key = min.key;
                tree.right = remove(tree.right, min);
            }
        } else {
            AVLTreeNode<T> tmp = tree;
            tree = (tree.left!=null) ? tree.left : tree.right;

            tmp = null;
        }
    }

    return tree;
}

public void remove(T key) {
    AVLTreeNode<T> z; 

    if ((z = search(mRoot, key)) != null)
        mRoot = remove(mRoot, z);
}
複製代碼

 

完整的實現代碼
AVL樹的實現文件(AVRTree.java)

複製代碼
  1 /**
  2  * Java 語言: AVL樹
  3  *
  4  * @author skywang
  5  * @date 2013/11/07
  6  */
  7 
  8 public class AVLTree<T extends Comparable<T>> {
  9     private AVLTreeNode<T> mRoot;    // 根結點
 10 
 11     // AVL樹的節點(內部類)
 12     class AVLTreeNode<T extends Comparable<T>> {
 13         T key;                // 關鍵字(鍵值)
 14         int height;         // 高度
 15         AVLTreeNode<T> left;    // 左孩子
 16         AVLTreeNode<T> right;    // 右孩子
 17 
 18         public AVLTreeNode(T key, AVLTreeNode<T> left, AVLTreeNode<T> right) {
 19             this.key = key;
 20             this.left = left;
 21             this.right = right;
 22             this.height = 0;
 23         }
 24     }
 25 
 26     // 構造函數
 27     public AVLTree() {
 28         mRoot = null;
 29     }
 30 
 31     /*
 32      * 獲取樹的高度
 33      */
 34     private int height(AVLTreeNode<T> tree) {
 35         if (tree != null)
 36             return tree.height;
 37 
 38         return 0;
 39     }
 40 
 41     public int height() {
 42         return height(mRoot);
 43     }
 44 
 45     /*
 46      * 比較兩個值的大小
 47      */
 48     private int max(int a, int b) {
 49         return a>b ? a : b;
 50     }
 51 
 52     /*
 53      * 前序遍歷"AVL樹"
 54      */
 55     private void preOrder(AVLTreeNode<T> tree) {
 56         if(tree != null) {
 57             System.out.print(tree.key+" ");
 58             preOrder(tree.left);
 59             preOrder(tree.right);
 60         }
 61     }
 62 
 63     public void preOrder() {
 64         preOrder(mRoot);
 65     }
 66 
 67     /*
 68      * 中序遍歷"AVL樹"
 69      */
 70     private void inOrder(AVLTreeNode<T> tree) {
 71         if(tree != null)
 72         {
 73             inOrder(tree.left);
 74             System.out.print(tree.key+" ");
 75             inOrder(tree.right);
 76         }
 77     }
 78 
 79     public void inOrder() {
 80         inOrder(mRoot);
 81     }
 82 
 83     /*
 84      * 後序遍歷"AVL樹"
 85      */
 86     private void postOrder(AVLTreeNode<T> tree) {
 87         if(tree != null) {
 88             postOrder(tree.left);
 89             postOrder(tree.right);
 90             System.out.print(tree.key+" ");
 91         }
 92     }
 93 
 94     public void postOrder() {
 95         postOrder(mRoot);
 96     }
 97 
 98     /*
 99      * (遞歸實現)查找"AVL樹x"中鍵值爲key的節點
100      */
101     private AVLTreeNode<T> search(AVLTreeNode<T> x, T key) {
102         if (x==null)
103             return x;
104 
105         int cmp = key.compareTo(x.key);
106         if (cmp < 0)
107             return search(x.left, key);
108         else if (cmp > 0)
109             return search(x.right, key);
110         else
111             return x;
112     }
113 
114     public AVLTreeNode<T> search(T key) {
115         return search(mRoot, key);
116     }
117 
118     /*
119      * (非遞歸實現)查找"AVL樹x"中鍵值爲key的節點
120      */
121     private AVLTreeNode<T> iterativeSearch(AVLTreeNode<T> x, T key) {
122         while (x!=null) {
123             int cmp = key.compareTo(x.key);
124 
125             if (cmp < 0)
126                 x = x.left;
127             else if (cmp > 0)
128                 x = x.right;
129             else
130                 return x;
131         }
132 
133         return x;
134     }
135 
136     public AVLTreeNode<T> iterativeSearch(T key) {
137         return iterativeSearch(mRoot, key);
138     }
139 
140     /* 
141      * 查找最小結點:返回tree爲根結點的AVL樹的最小結點。
142      */
143     private AVLTreeNode<T> minimum(AVLTreeNode<T> tree) {
144         if (tree == null)
145             return null;
146 
147         while(tree.left != null)
148             tree = tree.left;
149         return tree;
150     }
151 
152     public T minimum() {
153         AVLTreeNode<T> p = minimum(mRoot);
154         if (p != null)
155             return p.key;
156 
157         return null;
158     }
159      
160     /* 
161      * 查找最大結點:返回tree爲根結點的AVL樹的最大結點。
162      */
163     private AVLTreeNode<T> maximum(AVLTreeNode<T> tree) {
164         if (tree == null)
165             return null;
166 
167         while(tree.right != null)
168             tree = tree.right;
169         return tree;
170     }
171 
172     public T maximum() {
173         AVLTreeNode<T> p = maximum(mRoot);
174         if (p != null)
175             return p.key;
176 
177         return null;
178     }
179 
180     /*
181      * LL:左左對應的情況(左單旋轉)。
182      *
183      * 返回值:旋轉後的根節點
184      */
185     private AVLTreeNode<T> leftLeftRotation(AVLTreeNode<T> k2) {
186         AVLTreeNode<T> k1;
187 
188         k1 = k2.left;
189         k2.left = k1.right;
190         k1.right = k2;
191 
192         k2.height = max( height(k2.left), height(k2.right)) + 1;
193         k1.height = max( height(k1.left), k2.height) + 1;
194 
195         return k1;
196     }
197 
198     /*
199      * RR:右右對應的情況(右單旋轉)。
200      *
201      * 返回值:旋轉後的根節點
202      */
203     private AVLTreeNode<T> rightRightRotation(AVLTreeNode<T> k1) {
204         AVLTreeNode<T> k2;
205 
206         k2 = k1.right;
207         k1.right = k2.left;
208         k2.left = k1;
209 
210         k1.height = max( height(k1.left), height(k1.right)) + 1;
211         k2.height = max( height(k2.right), k1.height) + 1;
212 
213         return k2;
214     }
215 
216     /*
217      * LR:左右對應的情況(左雙旋轉)。
218      *
219      * 返回值:旋轉後的根節點
220      */
221     private AVLTreeNode<T> leftRightRotation(AVLTreeNode<T> k3) {
222         k3.left = rightRightRotation(k3.left);
223 
224         return leftLeftRotation(k3);
225     }
226 
227     /*
228      * RL:右左對應的情況(右雙旋轉)。
229      *
230      * 返回值:旋轉後的根節點
231      */
232     private AVLTreeNode<T> rightLeftRotation(AVLTreeNode<T> k1) {
233         k1.right = leftLeftRotation(k1.right);
234 
235         return rightRightRotation(k1);
236     }
237 
238     /* 
239      * 將結點插入到AVL樹中,並返回根節點
240      *
241      * 參數說明:
242      *     tree AVL樹的根結點
243      *     key 插入的結點的鍵值
244      * 返回值:
245      *     根節點
246      */
247     private AVLTreeNode<T> insert(AVLTreeNode<T> tree, T key) {
248         if (tree == null) {
249             // 新建節點
250             tree = new AVLTreeNode<T>(key, null, null);
251             if (tree==null) {
252                 System.out.println("ERROR: create avltree node failed!");
253                 return null;
254             }
255         } else {
256             int cmp = key.compareTo(tree.key);
257 
258                if (cmp < 0) {    // 應該將key插入到"tree的左子樹"的情況
259                 tree.left = insert(tree.left, key);
260                 // 插入節點後,若AVL樹失去平衡,則進行相應的調節。
261                 if (height(tree.left) - height(tree.right) == 2) {
262                     if (key.compareTo(tree.left.key) < 0)
263                         tree = leftLeftRotation(tree);
264                     else
265                         tree = leftRightRotation(tree);
266                 }
267             } else if (cmp > 0) {    // 應該將key插入到"tree的右子樹"的情況
268                 tree.right = insert(tree.right, key);
269                 // 插入節點後,若AVL樹失去平衡,則進行相應的調節。
270                 if (height(tree.right) - height(tree.left) == 2) {
271                     if (key.compareTo(tree.right.key) > 0)
272                         tree = rightRightRotation(tree);
273                     else
274                         tree = rightLeftRotation(tree);
275                 }
276             } else {    // cmp==0
277                 System.out.println("添加失敗:不允許添加相同的節點!");
278             }
279         }
280 
281         tree.height = max( height(tree.left), height(tree.right)) + 1;
282 
283         return tree;
284     }
285 
286     public void insert(T key) {
287         mRoot = insert(mRoot, key);
288     }
289 
290     /* 
291      * 刪除結點(z),返回根節點
292      *
293      * 參數說明:
294      *     tree AVL樹的根結點
295      *     z 待刪除的結點
296      * 返回值:
297      *     根節點
298      */
299     private AVLTreeNode<T> remove(AVLTreeNode<T> tree, AVLTreeNode<T> z) {
300         // 根爲空 或者 沒有要刪除的節點,直接返回null。
301         if (tree==null || z==null)
302             return null;
303 
304         int cmp = z.key.compareTo(tree.key);
305         if (cmp < 0) {        // 待刪除的節點在"tree的左子樹"中
306             tree.left = remove(tree.left, z);
307             // 刪除節點後,若AVL樹失去平衡,則進行相應的調節。
308             if (height(tree.right) - height(tree.left) == 2) {
309                 AVLTreeNode<T> r =  tree.right;
310                 if (height(r.left) > height(r.right))
311                     tree = rightLeftRotation(tree);
312                 else
313                     tree = rightRightRotation(tree);
314             }
315         } else if (cmp > 0) {    // 待刪除的節點在"tree的右子樹"中
316             tree.right = remove(tree.right, z);
317             // 刪除節點後,若AVL樹失去平衡,則進行相應的調節。
318             if (height(tree.left) - height(tree.right) == 2) {
319                 AVLTreeNode<T> l =  tree.left;
320                 if (height(l.right) > height(l.left))
321                     tree = leftRightRotation(tree);
322                 else
323                     tree = leftLeftRotation(tree);
324             }
325         } else {    // tree是對應要刪除的節點。
326             // tree的左右孩子都非空
327             if ((tree.left!=null) && (tree.right!=null)) {
328                 if (height(tree.left) > height(tree.right)) {
329                     // 如果tree的左子樹比右子樹高;
330                     // 則(01)找出tree的左子樹中的最大節點
331                     //   (02)將該最大節點的值賦值給tree。
332                     //   (03)刪除該最大節點。
333                     // 這類似於用"tree的左子樹中最大節點"做"tree"的替身;
334                     // 採用這種方式的好處是:刪除"tree的左子樹中最大節點"之後,AVL樹仍然是平衡的。
335                     AVLTreeNode<T> max = maximum(tree.left);
336                     tree.key = max.key;
337                     tree.left = remove(tree.left, max);
338                 } else {
339                     // 如果tree的左子樹不比右子樹高(即它們相等,或右子樹比左子樹高1)
340                     // 則(01)找出tree的右子樹中的最小節點
341                     //   (02)將該最小節點的值賦值給tree。
342                     //   (03)刪除該最小節點。
343                     // 這類似於用"tree的右子樹中最小節點"做"tree"的替身;
344                     // 採用這種方式的好處是:刪除"tree的右子樹中最小節點"之後,AVL樹仍然是平衡的。
345                     AVLTreeNode<T> min = maximum(tree.right);
346                     tree.key = min.key;
347                     tree.right = remove(tree.right, min);
348                 }
349             } else {
350                 AVLTreeNode<T> tmp = tree;
351                 tree = (tree.left!=null) ? tree.left : tree.right;
352                 tmp = null;
353             }
354         }
355 
356         return tree;
357     }
358 
359     public void remove(T key) {
360         AVLTreeNode<T> z; 
361 
362         if ((z = search(mRoot, key)) != null)
363             mRoot = remove(mRoot, z);
364     }
365 
366     /* 
367      * 銷燬AVL樹
368      */
369     private void destroy(AVLTreeNode<T> tree) {
370         if (tree==null)
371             return ;
372 
373         if (tree.left != null)
374             destroy(tree.left);
375         if (tree.right != null)
376             destroy(tree.right);
377 
378         tree = null;
379     }
380 
381     public void destroy() {
382         destroy(mRoot);
383     }
384 
385     /*
386      * 打印"二叉查找樹"
387      *
388      * key        -- 節點的鍵值 
389      * direction  --  0,表示該節點是根節點;
390      *               -1,表示該節點是它的父結點的左孩子;
391      *                1,表示該節點是它的父結點的右孩子。
392      */
393     private void print(AVLTreeNode<T> tree, T key, int direction) {
394         if(tree != null) {
395             if(direction==0)    // tree是根節點
396                 System.out.printf("%2d is root\n", tree.key, key);
397             else                // tree是分支節點
398                 System.out.printf("%2d is %2d's %6s child\n", tree.key, key, direction==1?"right" : "left");
399 
400             print(tree.left, tree.key, -1);
401             print(tree.right,tree.key,  1);
402         }
403     }
404 
405     public void print() {
406         if (mRoot != null)
407             print(mRoot, mRoot.key, 0);
408     }
409 }
複製代碼
View Code

AVL樹的測試程序(AVLTreeTest.java)

 1 /**
 2  * Java 語言: AVL樹
 3  *
 4  * @author skywang
 5  * @date 2013/11/07
 6  */
 7 
 8 public class AVLTreeTest {
 9     private static int arr[]= {3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9};
10 
11     public static void main(String[] args) {
12         int i;
13         AVLTree<Integer> tree = new AVLTree<Integer>();
14 
15         System.out.printf("== 依次添加: ");
16         for(i=0; i<arr.length; i++) {
17             System.out.printf("%d ", arr[i]);
18             tree.insert(arr[i]);
19         }
20 
21         System.out.printf("\n== 前序遍歷: ");
22         tree.preOrder();
23 
24         System.out.printf("\n== 中序遍歷: ");
25         tree.inOrder();
26 
27         System.out.printf("\n== 後序遍歷: ");
28         tree.postOrder();
29         System.out.printf("\n");
30 
31         System.out.printf("== 高度: %d\n", tree.height());
32         System.out.printf("== 最小值: %d\n", tree.minimum());
33         System.out.printf("== 最大值: %d\n", tree.maximum());
34         System.out.printf("== 樹的詳細信息: \n");
35         tree.print();
36 
37         i = 8;
38         System.out.printf("\n== 刪除根節點: %d", i);
39         tree.remove(i);
40 
41         System.out.printf("\n== 高度: %d", tree.height());
42         System.out.printf("\n== 中序遍歷: ");
43         tree.inOrder();
44         System.out.printf("\n== 樹的詳細信息: \n");
45         tree.print();
46 
47         // 銷燬二叉樹
48         tree.destroy();
49     }
50 }

View Code

 

AVL樹的Java測試程序

AVL樹的測試程序運行結果如下:

複製代碼
== 依次添加: 3 2 1 4 5 6 7 16 15 14 13 12 11 10 8 9 
== 前序遍歷: 7 4 2 1 3 6 5 13 11 9 8 10 12 15 14 16 
== 中序遍歷: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
== 後序遍歷: 1 3 2 5 6 4 8 10 9 12 11 14 16 15 13 7 
== 高度: 5
== 最小值: 1
== 最大值: 16
== 樹的詳細信息: 
 7 is root
 4 is  7's   left child
 2 is  4's   left child
 1 is  2's   left child
 3 is  2's  right child
 6 is  4's  right child
 5 is  6's   left child
13 is  7's  right child
11 is 13's   left child
 9 is 11's   left child
 8 is  9's   left child
10 is  9's  right child
12 is 11's  right child
15 is 13's  right child
14 is 15's   left child
16 is 15's  right child

== 刪除根節點: 8
== 高度: 5
== 中序遍歷: 1 2 3 4 5 6 7 9 10 11 12 13 14 15 16 
== 樹的詳細信息: 
 7 is root
 4 is  7's   left child
 2 is  4's   left child
 1 is  2's   left child
 3 is  2's  right child
 6 is  4's  right child
 5 is  6's   left child
13 is  7's  right child
11 is 13's   left child
 9 is 11's   left child
10 is  9's  right child
12 is 11's  right child
15 is 13's  right child
14 is 15's   left child
16 is 15's  right child
複製代碼

 

下面,我們對測試程序的流程進行分析!

1. 新建AVL樹

2. 依次添加”3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9” 到AVL樹中。

2.01 添加3,2
添加3,2都不會破壞AVL樹的平衡性。

 

2.02 添加1
添加1之後,AVL樹失去平衡(LL),此時需要對AVL樹進行旋轉(LL旋轉)。旋轉過程如下:

 

2.03 添加4
添加4不會破壞AVL樹的平衡性。

 

2.04 添加5
添加5之後,AVL樹失去平衡(RR),此時需要對AVL樹進行旋轉(RR旋轉)。旋轉過程如下:

 

2.05 添加6
添加6之後,AVL樹失去平衡(RR),此時需要對AVL樹進行旋轉(RR旋轉)。旋轉過程如下:

 

2.06 添加7
添加7之後,AVL樹失去平衡(RR),此時需要對AVL樹進行旋轉(RR旋轉)。旋轉過程如下:

 

2.07 添加16
添加16不會破壞AVL樹的平衡性。

 

2.08 添加15
添加15之後,AVL樹失去平衡(RR),此時需要對AVL樹進行旋轉(RR旋轉)。旋轉過程如下:

 

2.09 添加14
添加14之後,AVL樹失去平衡(RL),此時需要對AVL樹進行旋轉(RL旋轉)。旋轉過程如下:

 

2.10 添加13
添加13之後,AVL樹失去平衡(RR),此時需要對AVL樹進行旋轉(RR旋轉)。旋轉過程如下:

 

2.11 添加12
添加12之後,AVL樹失去平衡(LL),此時需要對AVL樹進行旋轉(LL旋轉)。旋轉過程如下:

 

2.12 添加11
添加11之後,AVL樹失去平衡(LL),此時需要對AVL樹進行旋轉(LL旋轉)。旋轉過程如下:

 

2.13 添加10
添加10之後,AVL樹失去平衡(LL),此時需要對AVL樹進行旋轉(LL旋轉)。旋轉過程如下:

 

2.14 添加8
添加8不會破壞AVL樹的平衡性。

 

2.15 添加9
但是添加9之後,AVL樹失去平衡(LR),此時需要對AVL樹進行旋轉(LR旋轉)。旋轉過程如下:

 

3. 打印樹的信息

輸出下面樹的信息:


前序遍歷: 7 4 2 1 3 6 5 13 11 9 8 10 12 15 14 16 
中序遍歷: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
後序遍歷: 1 3 2 5 6 4 8 10 9 12 11 14 16 15 13 7 
高度: 5
最小值: 1
最大值: 16

 

4. 刪除節點8

刪除操作並不會造成AVL樹的不平衡。

刪除節點8之後,再打印該AVL樹的信息。
高度: 5
中序遍歷: 1 2 3 4 5 6 7 9 10 11 12 13 14 15 16

補充:

AVRTree.java 355行
刪除節點時,應加上:
if(tree != null) tree.height = max(height(tree.left), height(tree.right)) + 1;
否則,從調整子樹向上的節點height沒有更新。

例如,輸入序列:5,3,6,2,4,7,1
刪除,4
正確的輸出應是:
== 依次添加: 5 3 6 2 4 7 1
== 前序遍歷: 5 3 2 1 4 6 7
== 中序遍歷: 1 2 3 4 5 6 7
== 後序遍歷: 1 2 4 3 7 6 5
== 高度: 4
== 最小值: 1
== 最大值: 7
== 樹的詳細信息:
5 is root
3 is 5’s left child
2 is 3’s left child
1 is 2’s left child
4 is 3’s right child
6 is 5’s right child
7 is 6’s right child

== 刪除根節點: 4
== 高度: 3
== 中序遍歷: 1 2 3 5 6 7
== 樹的詳細信息:
5 is root
2 is 5’s left child
1 is 2’s left child
3 is 2’s right child
6 is 5’s right child
7 is 6’s right child

如果不加height更新,輸出的高度爲4,就不對了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章