AVL树的插入和删除

一、AVL 树

  在计算机科学中,AVL树是最早被发明的自平衡二叉查找树。在AVL树中,任一节点对应的两棵子树的最大高度差为 1,因此它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下的时间复杂度都是 O(log(n))。插入和删除元素的操作则可能需要借由一次或多次树旋转,以实现树的重新平衡。

  节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子 1、0 或 -1 的节点被认为是平衡的。带有平衡因子 -2 或 2 的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

  大多数 BST 操作(例如,搜索,最大,最小,插入,删除等)花费 O(h) 时间,其中 h 是 BST 的高度。对于偏斜的二叉树,这些操作的成本可能变为 O(n)。如果确保每次插入和删除后树的高度都保持 O(log2n),则可以保证所有这些操作的 O(log2n)上限。AVL树的高度始终为 O(log2n),其中 n 是树中的节点数。

二、AVL 树的旋转

  AVL 树在普通的插入和删除节点时都会使得树失去平衡,这时我们需要一些操作来把树恢复平衡,这些操作叫做AVL树的旋转,分为左旋右旋

  T1,T2 和 T3 是树 y(左边) 或 x(右边) 的子树:

     y                                 x
    / \       Right Rotation          /  \
   x   T3     - - - - - - - >        T1   y 
  / \         < - - - - - - -            / \
 T1  T2       Left Rotation            T2  T3

  以上两个树中的键都遵循以下顺序(二叉查找树的性质):

  keys(T1) < key(x) < keys(T2) < key(y) < keys(T3)。

 1     /**
 2      * 右旋转以y为根的子树
 3      *
 4      * @param y
 5      * @return
 6      */
 7     private Node rightRoate(Node y) {
 8         Node x = y.left;
 9         Node T2 = x.right;
10 
11         /* 执行旋转 */
12         x.right = y;
13         y.left = T2;
14 
15         /* 更新高度 */
16         y.height = max(height(y.left), height(y.right)) + 1;
17         x.height = max(height(x.left), height(x.right)) + 1;
18 
19         return x;
20     }
21 
22     /**
23      * 左旋转以x为根的子树
24      *
25      * @param x
26      * @return
27      */
28     private Node leftRoate(Node x) {
29         Node y = x.right;
30         Node T2 = y.left;
31 
32         /* 执行旋转 */
33         y.left = x;
34         x.right = T2;
35 
36         /* 更新高度 */
37         x.height = max(height(x.left), height(x.right)) + 1;
38         y.height = max(height(y.left), height(y.right)) + 1;
39 
40         return y;
41     }

三、AVL 树的插入操作

插入要遵循的步骤:

  新插入的节点为 w 

  1)对 w 执行标准 BST 插入。

  2)从 w 开始,向上移动并找到第一个不平衡节点。令 z 为第一个不平衡节点,y 为从 w 到 z 的路径中 z 的子代,x 为从 w 到 z 的路径中 z 的孙代。

  3)通过对以 z 为根的子树执行适当的旋转重新平衡树。可能有 4 种可能的情况需要处理,因为 x,y 和 z 可以 4 种方式排列。以下是可能的 4 种排列方式:

    a)y 是 z 的左子代,x 是 y 的左子代(左案例)

         z                                      y 
        / \                                   /   \
       y   T4      Right Rotate (z)          x      z
      / \          - - - - - - - - ->      /  \    /  \ 
     x   T3                               T1  T2  T3  T4
    / \
  T1   T2

    b)y 是 z 的左子代,x 是 y 的右子代(左案例)

     z                               z                           x
    / \                            /   \                        /  \ 
   y   T4  Left Rotate (y)        x    T4  Right Rotate(z)    y      z
  / \      - - - - - - - - ->    /  \      - - - - - - - ->  / \    / \
T1   x                          y    T3                    T1  T2 T3  T4
    / \                        / \
  T2   T3                    T1   T2

    c)y 是 z 的右子代,x 是 y 的右子代(右右案例)

  z                                y
 /  \                            /   \ 
T1   y     Left Rotate(z)       z      x
    /  \   - - - - - - - ->    / \    / \
   T2   x                     T1  T2 T3  T4
       / \
     T3  T4

    d)y 是 z 的右子代,x 是 y 的左子代(右左案例)

   z                            z                            x
  / \                          / \                          /  \ 
T1   y   Right Rotate (y)    T1   x      Left Rotate(z)   z      y
    / \  - - - - - - - - ->     /  \   - - - - - - - ->  / \    / \
   x   T4                      T2   y                  T1  T2  T3  T4
  / \                              /  \
T2   T3                           T3   T4

  插入操作:

 1     /**
 2      * AVL树的插入
 3      *
 4      * @param node
 5      * @param key
 6      * @return
 7      */
 8     private Node insert(Node node, int key) {
 9         /* 执行正常的BST插入 */
10         if (node == null)
11             return (new Node(key));
12 
13         if (key < node.key)
14             node.left = insert(node.left, key);
15         else if (key > node.key)
16             node.right = insert(node.right, key);
17         else    // 不允许重复的key
18             return node;
19 
20         /* 更新此祖先节点的高度 */
21         node.height = 1 + max(height(node.left), height(node.right));
22 
23         /* 获取此祖先的平衡因子以检查此节点是否变为不平衡 */
24         int balance = getBalance(node);
25 
26         /* 如果此节点变得不平衡,则存在有4种情况 */
27         if (balance > 1 && key < node.left.key) {
28             return rightRoate(node);
29         }
30 
31         if (balance < -1 && key > node.right.key) {
32             return leftRoate(node);
33         }
34 
35         if (balance > 1 && key > node.left.key) {
36             node.left = leftRoate(node.left);
37             return rightRoate(node);
38         }
39 
40         if (balance < -1 && key < node.right.key) {
41             node.right = rightRoate(node.right);
42             return leftRoate(node);
43         }
44 
45         return node;
46     }

四、AVL 树的删除操作

删除要遵循的步骤:

  令 w 为要删除的节点

  1)对 w 执行标准BST删除。

  2)从 w 开始,向上移动并找到第一个不平衡节点。令 z 为第一个不平衡节点,y 为 z 的较大孩子,x 为 y 的较大孩子。请注意,x 和 y 的定义与此处的插入不同。

  3)通过对以 z 为根的子树执行适当的旋转来重新平衡树。有 4 种可能的情况需要处理,因为 x,y 和 z 可以 4 种方式排列。以下是可能的 4 种排列方式:

    a)y 是 z 的左子代,x是y的左子代(左案例)

         z                                      y 
        / \                                   /   \
       y   T4      Right Rotate (z)          x      z
      / \          - - - - - - - - ->      /  \    /  \ 
     x   T3                               T1  T2  T3  T4
    / \
  T1   T2

    b)y 是 z 的左子代,x 是 y 的右子代(左案例)

     z                               z                           x
    / \                            /   \                        /  \ 
   y   T4  Left Rotate (y)        x    T4  Right Rotate(z)    y      z
  / \      - - - - - - - - ->    /  \      - - - - - - - ->  / \    / \
T1   x                          y    T3                    T1  T2 T3  T4
    / \                        / \
  T2   T3                    T1   T2

    c)y 是 z 的右子代,x 是 y 的右子代(右右案例)

  z                                y
 /  \                            /   \ 
T1   y     Left Rotate(z)       z      x
    /  \   - - - - - - - ->    / \    / \
   T2   x                     T1  T2 T3  T4
       / \
     T3  T4

    d)y 是 z 的右子代,x 是 y 的左代子(右左案例)

 

   z                            z                            x
  / \                          / \                          /  \ 
T1   y   Right Rotate (y)    T1   x      Left Rotate(z)   z      y
    / \  - - - - - - - - ->     /  \   - - - - - - - ->  / \    / \
   x   T4                      T2   y                  T1  T2  T3  T4
  / \                              /  \
T2   T3                           T3   T4

  与插入不同,在删除中,在 z 处进行旋转后,可能必须在 z 的祖先处进行旋转。因此,我们必须继续追踪路径,直到到达根为止。

  删除操作:

 1     /**
 2      * AVL树的删除
 3      *
 4      * @param N
 5      * @return
 6      */
 7     private Node deleteNode(Node root, int key) {
 8         if (root == null)
 9             return root;
10         /* 如果要删除的key小于root的key,则它位于左子树中 */
11         if (key < root.key)
12             root.left = deleteNode(root.left, key);
13 
14         /* 如果要删除的key大于root的key,则它位于右子树中 */
15         else if (key > root.key)
16             root.right = deleteNode(root.right, key);
17 
18         /* 如果key与root的key相同,则这个节点要被删除 */
19         else {
20             /* 只有一个孩子或没有孩子的节点 */
21             if ((root.left == null) || (root.right == null)) {
22                 Node temp = null;
23                 if (temp == root.left)
24                     temp = root.right;
25                 else
26                     temp = root.left;
27 
28                 /* 没有孩子的情况 */
29                 if (temp == null) {
30                     temp = root;
31                     root = null;
32                 } else {    // 只有一个孩子
33                     root = temp; // 复制非空孩子的内容
34                 }
35 
36             } else {
37                 /* 有两个子节点的节点:获取后继节点(在右侧子树中最小) */
38                 Node temp = minValueNode(root.right);
39                 /* 将后继节点的数据复制到此节点 */
40                 root.key = temp.key;
41                 /* 删除后继节点 */
42                 root.right = deleteNode(root.right, temp.key);
43             }
44         }
45 
46         /* 如果树只有一个节点,则返回 */
47         if (root == null)
48             return root;
49 
50         /* 更新当前节点的高度 */
51         root.height = max(height(root.left), height(root.right)) + 1;
52         /* 获取此节点的平衡因素 */
53         int balance = getBalance(root);
54 
55         /* 如果此节点变得不平衡,则有4种情况 */
56         if (balance > 1 && getBalance(root.left) >= 0) {
57             return rightRoate(root);
58         }
59 
60         if (balance > 1 && getBalance(root.left) < 0) {
61             root.left = leftRoate(root.left);
62             return rightRoate(root);
63         }
64 
65         if (balance < -1 && getBalance(root.right) <= 0) {
66             return leftRoate(root);
67         }
68 
69         if (balance < -1 && getBalance(root.right) > 0) {
70             root.right = rightRoate(root.right);
71             return leftRoate(root);
72         }
73 
74         return root;
75     }

本文源代码:

  1 package algorithm;
  2 
  3 /**
  4  * 自平衡二叉树,左右子树的高度差不大于1
  5  */
  6 public class AVLTree {
  7 
  8     private Node root;
  9 
 10     /**
 11      * 树高度
 12      *
 13      * @param N
 14      * @return
 15      */
 16     private int height(Node N) {
 17         if (N == null) {
 18             return 0;
 19         }
 20         return N.height;
 21     }
 22 
 23     private int max(int a, int b) {
 24         return Math.max(a, b);
 25     }
 26 
 27     /**
 28      * 右旋转以y为根的子树
 29      *
 30      * @param y
 31      * @return
 32      */
 33     private Node rightRoate(Node y) {
 34         Node x = y.left;
 35         Node T2 = x.right;
 36 
 37         /* 执行旋转 */
 38         x.right = y;
 39         y.left = T2;
 40 
 41         /* 更新高度 */
 42         y.height = max(height(y.left), height(y.right)) + 1;
 43         x.height = max(height(x.left), height(x.right)) + 1;
 44 
 45         return x;
 46     }
 47 
 48     /**
 49      * 左旋转以x为根的子树
 50      *
 51      * @param x
 52      * @return
 53      */
 54     private Node leftRoate(Node x) {
 55         Node y = x.right;
 56         Node T2 = y.left;
 57 
 58         /* 执行旋转 */
 59         y.left = x;
 60         x.right = T2;
 61 
 62         /* 更新高度 */
 63         x.height = max(height(x.left), height(x.right)) + 1;
 64         y.height = max(height(y.left), height(y.right)) + 1;
 65 
 66         return y;
 67     }
 68 
 69     /**
 70      * 获取N结点的平衡
 71      *
 72      * @param N
 73      * @return
 74      */
 75     private int getBalance(Node N) {
 76         if (N == null)
 77             return 0;
 78 
 79         return height(N.left) - height(N.right);
 80     }
 81 
 82     /**
 83      * AVL树的插入
 84      *
 85      * @param node
 86      * @param key
 87      * @return
 88      */
 89     private Node insert(Node node, int key) {
 90         /* 执行正常的BST插入 */
 91         if (node == null)
 92             return (new Node(key));
 93 
 94         if (key < node.key)
 95             node.left = insert(node.left, key);
 96         else if (key > node.key)
 97             node.right = insert(node.right, key);
 98         else    // 不允许重复的key
 99             return node;
100 
101         /* 更新此祖先节点的高度 */
102         node.height = 1 + max(height(node.left), height(node.right));
103 
104         /* 获取此祖先的平衡因子以检查此节点是否变为不平衡 */
105         int balance = getBalance(node);
106 
107         /* 如果此节点变得不平衡,则存在有4种情况 */
108         if (balance > 1 && key < node.left.key) {
109             return rightRoate(node);
110         }
111 
112         if (balance < -1 && key > node.right.key) {
113             return leftRoate(node);
114         }
115 
116         if (balance > 1 && key > node.left.key) {
117             node.left = leftRoate(node.left);
118             return rightRoate(node);
119         }
120 
121         if (balance < -1 && key < node.right.key) {
122             node.right = rightRoate(node.right);
123             return leftRoate(node);
124         }
125 
126         return node;
127     }
128 
129     /**
130      * 给定一个非空的二叉查找树,返回树中最小key值的结点(注意无需遍历整个树)
131      *
132      * @param node
133      * @return
134      */
135     private Node minValueNode(Node node) {
136         Node current = node;
137 
138         while (current.left != null)
139             current = current.left;
140 
141         return current;
142     }
143 
144     /**
145      * AVL树的删除
146      *
147      * @param N
148      * @return
149      */
150     private Node deleteNode(Node root, int key) {
151         if (root == null)
152             return root;
153         /* 如果要删除的key小于root的key,则它位于左子树中 */
154         if (key < root.key)
155             root.left = deleteNode(root.left, key);
156 
157         /* 如果要删除的key大于root的key,则它位于右子树中 */
158         else if (key > root.key)
159             root.right = deleteNode(root.right, key);
160 
161         /* 如果key与root的key相同,则这个节点要被删除 */
162         else {
163             /* 只有一个孩子或没有孩子的节点 */
164             if ((root.left == null) || (root.right == null)) {
165                 Node temp = null;
166                 if (temp == root.left)
167                     temp = root.right;
168                 else
169                     temp = root.left;
170 
171                 /* 没有孩子的情况 */
172                 if (temp == null) {
173                     temp = root;
174                     root = null;
175                 } else {    // 只有一个孩子
176                     root = temp; // 复制非空孩子的内容
177                 }
178 
179             } else {
180                 /* 有两个子节点的节点:获取后继节点(在右侧子树中最小) */
181                 Node temp = minValueNode(root.right);
182                 /* 将后继节点的数据复制到此节点 */
183                 root.key = temp.key;
184                 /* 删除后继节点 */
185                 root.right = deleteNode(root.right, temp.key);
186             }
187         }
188 
189         /* 如果树只有一个节点,则返回 */
190         if (root == null)
191             return root;
192 
193         /* 更新当前节点的高度 */
194         root.height = max(height(root.left), height(root.right)) + 1;
195         /* 获取此节点的平衡因素 */
196         int balance = getBalance(root);
197 
198         /* 如果此节点变得不平衡,则有4种情况 */
199         if (balance > 1 && getBalance(root.left) >= 0) {
200             return rightRoate(root);
201         }
202 
203         if (balance > 1 && getBalance(root.left) < 0) {
204             root.left = leftRoate(root.left);
205             return rightRoate(root);
206         }
207 
208         if (balance < -1 && getBalance(root.right) <= 0) {
209             return leftRoate(root);
210         }
211 
212         if (balance < -1 && getBalance(root.right) > 0) {
213             root.right = rightRoate(root.right);
214             return leftRoate(root);
215         }
216 
217         return root;
218     }
219 
220     /**
221      * 先序遍历
222      *
223      * @param node
224      */
225     private void preOrder(Node node) {
226         if (node != null) {
227             System.out.print(node.key + " ");
228             preOrder(node.left);
229             preOrder(node.right);
230         }
231     }
232 
233     public static void main(String[] args) {
234 
235         /* ---------------------------------------------------- */
236 
237         AVLTree tree = new AVLTree();
238 
239         /* 构造AVL树 */
240         tree.root = tree.insert(tree.root, 9);
241         tree.root = tree.insert(tree.root, 5);
242         tree.root = tree.insert(tree.root, 10);
243         tree.root = tree.insert(tree.root, 0);
244         tree.root = tree.insert(tree.root, 6);
245         tree.root = tree.insert(tree.root, 11);
246         tree.root = tree.insert(tree.root, -1);
247         tree.root = tree.insert(tree.root, 1);
248         tree.root = tree.insert(tree.root, 2);
249 
250         /* 构造的AVL树:
251                  9
252                 / \
253                1   10
254               / \    \
255              0   5   11
256             /   / \
257            -1  2   6
258         */
259         System.out.println("Preorder traversal of constructed tree is : ");
260         tree.preOrder(tree.root);
261 
262         tree.root = tree.deleteNode(tree.root, 10);
263 
264         /* 删除10后的AVL树:
265                   1
266                 /   \
267                0     9
268               /     / \
269             -1     5   11
270             / \
271            2   6
272         */
273         System.out.println();
274         System.out.println("Preorder traversal after "+
275                 "deletion of 10 :");
276         tree.preOrder(tree.root);
277     }
278 }
279 
280 class Node {
281     int key, height;
282     Node left, right;
283 
284     Node(int d) {
285         key = d;
286         height = 1;
287     }
288 
289 }
View Code
posted @ 2019-12-05 23:40  賣贾笔的小男孩  阅读(2289)  评论(0编辑  收藏  举报