• 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
  • 相关阅读:
    [湖南集训]谈笑风生
    【SCOI2010】序列操作
    ●BZOJ 3994 [SDOI2015]约数个数和
    ●BZOJ 3309 DZY Loves Math
    ●UOJ 21 缩进优化
    ●BZOJ 2693 jzptab
    ●BZOJ 2154 Crash的数字表格
    ●BZOJ 3529 [Sdoi2014]数表
    ●2301 [HAOI2011] Problem b
    ●BZOJ 2820 YY的GCD
  • 原文地址:https://www.cnblogs.com/magic-sea/p/11992833.html
Copyright © 2020-2023  润新知