• 平衡二叉树(AVL树)


    平衡二叉树(AVL树)

    平衡二叉树简介:

      平衡树(Balance Tree,BT) 指的是,任意节点的子树的高度差都小于等于1。常见的符合平衡树的有,B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等。

      具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1, 并且左右两个子树都是-棵平衡二叉树。

      平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。可以保证查询效率高

    举例看下下面AVL树的特点吧:左右两个子树的高度差的绝对值不超过1

    第三棵树的左子树高度是3,右子树高度是1,3-1=2,所以第三个不是AVL树

    AVL树左旋

    AVL树左旋图解

     要求: 给你一个数列,创建出对应的平衡二叉树.数列 {4,3,6,5,7,8}

    AVL树左旋步骤:

    1. 创建一个新的节点,值为当前节点的值
    2. 把新节点的的左子树设置为原节点的左子树:4-->指向3
    3. 把新节点的右子树设置为当前节点的右子树的左子树
    4. 把当前节点的值:4 换成当前右子节点的值:6
    5. 把当前节点的右子树设为右子树的右子树
    6. 把当前节点的左子树设为新的节点:6-->指向4

    AVL树的高度计算

    核心:首先要把一棵树的高度以及左子树和右子树的高度计算出来

    Node类中添加这三个方法:

     1   /**
     2      * 返回以当前节点为根节点的树的高度
     3      *
     4      * @return 返回树的高度
     5      */
     6     public int heightTree() {
     7         // 比较左子树跟右子树的高度,返回最大的。+1是因为树本身还要站一层
     8         return Math.max(left == null ? 0 : left.heightTree(), right == null ? 0 : right.heightTree()) + 1;
     9     }
    10 
    11     /**
    12      * 返回左子树的高度
    13      *
    14      * @return 左子树高度
    15      */
    16     public int leftHeight() {
    17         if (left == null) {
    18             return 0;
    19         }
    20         return left.heightTree();
    21     }
    22 
    23     /**
    24      * 返回右子树的高度
    25      *
    26      * @return 右子树高度
    27      */
    28     public int rightHeight() {
    29         if (right == null) {
    30             return 0;
    31         }
    32         return right.heightTree();
    33     }
    View Code

    AVLTree类:直接用上个二叉排序树的树代码即可!

      1 class AVLTree {
      2     private Node root;
      3 
      4     public Node getRoot() {
      5         return root;
      6     }
      7 
      8     /**
      9      * 查找要删除的节点
     10      *
     11      * @param value
     12      * @return
     13      */
     14     public Node delSearch(int value) {
     15         if (root == null) {
     16             return null;
     17         } else {
     18             return root.delSearch(value);
     19         }
     20     }
     21 
     22     /**
     23      * 查找到要删除节点的父节点
     24      *
     25      * @param value
     26      * @return
     27      */
     28     public Node delSearchParent(int value) {
     29         if (root == null) {
     30             return null;
     31         } else {
     32             return root.delSearchParent(value);
     33         }
     34     }
     35 
     36     /**
     37      * 找到最小值并删除
     38      *
     39      * @param node
     40      * @return 返回删除节点的值
     41      */
     42     public int delRightTreeMin(Node node) {
     43         // 作一个辅助节点
     44         Node target = node;
     45         // 循环往左子树进行查找,就会找到最小值
     46         while (target.left != null) {
     47             target = target.left;
     48         }
     49         // 删除最小值
     50         delNode(target.value);
     51         // 返回最小值
     52         return target.value;
     53     }
     54 
     55     /**
     56      * 删除节点
     57      *
     58      * @param value
     59      */
     60     public void delNode(int value) {
     61         if (root == null) {
     62             return;
     63         } else {
     64             // 1、找到要删除的节点
     65             Node targetNode = delSearch(value);
     66             // 没有找到
     67             if (targetNode == null) {
     68                 return;
     69             }
     70             // 表示这颗二叉排序树只有一个节点(父节点)
     71             if (root.left == null && root.right == null) {
     72                 root = null;
     73                 return;
     74             }
     75 
     76             // 2、找到要删除节点的父节点
     77             Node parentNode = delSearchParent(value);
     78             // 表示要删除的节点是一个叶子节点
     79             if (targetNode.left == null && targetNode.right == null) {
     80                 // 继续判断这个叶子节点是父节点的左子节点还是右子节点
     81                 if (parentNode.left != null && parentNode.left.value == value) {
     82                     // 将这个叶子节点置为空
     83                     parentNode.left = null;
     84                 } else if (parentNode.right != null && parentNode.right.value == value) {
     85                     parentNode.right = null;
     86                 }
     87             } else if (targetNode.left != null && targetNode.right != null) {// 删除有两颗子树的节点
     88                 // 找到最小值
     89                 int minVal = delRightTreeMin(targetNode.right);
     90                 // 重置
     91                 targetNode.value = minVal;
     92             } else {// 删除只有一颗子树的节点
     93                 if (targetNode.left != null) {// 如果要删除的节点有左子节点
     94                     if (parentNode != null) {
     95                         // 待删除节点是父节点的左子节点
     96                         if (parentNode.left.value == value) {
     97                             parentNode.left = targetNode.left;
     98                         } else {// 待删除节点是父节点的右子节点
     99                             parentNode.right = targetNode.left;
    100                         }
    101                     } else {
    102                         root = targetNode.left;
    103                     }
    104                 } else {// 如果要删除的节点有右子节点
    105                     if (parentNode != null) {
    106                         // 待删除节点是父节点的左子节点
    107                         if (parentNode.left.value == value) {
    108                             parentNode.left = targetNode.right;
    109                         } else {// 待删除节点是父节点的右子节点
    110                             parentNode.right = targetNode.right;
    111                         }
    112                     } else {
    113                         root = targetNode.right;
    114                     }
    115                 }
    116             }
    117         }
    118     }
    119 
    120     /**
    121      * 添加节点的方法
    122      *
    123      * @param node
    124      */
    125     public void addNode(Node node) {
    126         // root节点为空,就让root成为根节点
    127         if (root == null) {
    128             root = node;
    129         } else {// root节点不为空,就继续向树中添加节点
    130             root.addNode(node);
    131         }
    132     }
    133 
    134     /**
    135      * 进行中序遍历
    136      */
    137     public void infixOrder() {
    138         if (root != null) {
    139             root.infixOrder();
    140         } else {
    141             System.out.println("二叉树为空,无法进行排序!");
    142         }
    143     }
    144 }
    View Code

     测试树的高度:

    public static void main(String[] args) {
        int[] arr = {4, 3, 6, 5, 7, 8};
        // 创建AVL树对象
        AVLTree avlTree = new AVLTree();
        for (int i = 0; i < arr.length; i++) {
            // 添加节点
            avlTree.addNode(new Node(arr[i]));
        }
        // 中序遍历
        System.out.println("======================中序遍历======================");
        avlTree.infixOrder();
        System.out.println("======================没有平衡处理前======================");
        System.out.println("没有平衡处理前树的高度:" + avlTree.getRoot().heightTree());
        System.out.println("没有平衡处理前树的左子树高度:" + avlTree.getRoot().leftHeight());
        System.out.println("没有平衡处理前树的右子树高度:" + avlTree.getRoot().rightHeight());
    }
    

    附上总体代码实现:

      1 package Demo11_平衡二叉树_AVL树;
      2 
      3 /**
      4  * @author zhangzhixi
      5  * @date 2021/3/12 22:58
      6  */
      7 public class AVLTreeDemo {
      8     public static void main(String[] args) {
      9         int[] arr = {4, 3, 6, 5, 7, 8};
     10 
     11         // 创建AVL树对象
     12         AVLTree avlTree = new AVLTree();
     13 
     14         for (int i = 0; i < arr.length; i++) {
     15             // 添加节点
     16             avlTree.addNode(new Node(arr[i]));
     17         }
     18 
     19         // 中序遍历
     20         System.out.println("======================中序遍历======================");
     21         avlTree.infixOrder();
     22         System.out.println("======================没有平衡处理前======================");
     23         System.out.println("没有平衡处理前树的高度:" + avlTree.getRoot().heightTree());
     24         System.out.println("没有平衡处理前树的左子树高度:" + avlTree.getRoot().leftHeight());
     25         System.out.println("没有平衡处理前树的右子树高度:" + avlTree.getRoot().rightHeight());
     26     }
     27 }
     28 
     29 class AVLTree {
     30     private Node root;
     31 
     32     public Node getRoot() {
     33         return root;
     34     }
     35 
     36     /**
     37      * 查找要删除的节点
     38      *
     39      * @param value
     40      * @return
     41      */
     42     public Node delSearch(int value) {
     43         if (root == null) {
     44             return null;
     45         } else {
     46             return root.delSearch(value);
     47         }
     48     }
     49 
     50     /**
     51      * 查找到要删除节点的父节点
     52      *
     53      * @param value
     54      * @return
     55      */
     56     public Node delSearchParent(int value) {
     57         if (root == null) {
     58             return null;
     59         } else {
     60             return root.delSearchParent(value);
     61         }
     62     }
     63 
     64     /**
     65      * 找到最小值并删除
     66      *
     67      * @param node
     68      * @return 返回删除节点的值
     69      */
     70     public int delRightTreeMin(Node node) {
     71         // 作一个辅助节点
     72         Node target = node;
     73         // 循环往左子树进行查找,就会找到最小值
     74         while (target.left != null) {
     75             target = target.left;
     76         }
     77         // 删除最小值
     78         delNode(target.value);
     79         // 返回最小值
     80         return target.value;
     81     }
     82 
     83     /**
     84      * 删除节点
     85      *
     86      * @param value
     87      */
     88     public void delNode(int value) {
     89         if (root == null) {
     90             return;
     91         } else {
     92             // 1、找到要删除的节点
     93             Node targetNode = delSearch(value);
     94             // 没有找到
     95             if (targetNode == null) {
     96                 return;
     97             }
     98             // 表示这颗二叉排序树只有一个节点(父节点)
     99             if (root.left == null && root.right == null) {
    100                 root = null;
    101                 return;
    102             }
    103 
    104             // 2、找到要删除节点的父节点
    105             Node parentNode = delSearchParent(value);
    106             // 表示要删除的节点是一个叶子节点
    107             if (targetNode.left == null && targetNode.right == null) {
    108                 // 继续判断这个叶子节点是父节点的左子节点还是右子节点
    109                 if (parentNode.left != null && parentNode.left.value == value) {
    110                     // 将这个叶子节点置为空
    111                     parentNode.left = null;
    112                 } else if (parentNode.right != null && parentNode.right.value == value) {
    113                     parentNode.right = null;
    114                 }
    115             } else if (targetNode.left != null && targetNode.right != null) {// 删除有两颗子树的节点
    116                 // 找到最小值
    117                 int minVal = delRightTreeMin(targetNode.right);
    118                 // 重置
    119                 targetNode.value = minVal;
    120             } else {// 删除只有一颗子树的节点
    121                 if (targetNode.left != null) {// 如果要删除的节点有左子节点
    122                     if (parentNode != null) {
    123                         // 待删除节点是父节点的左子节点
    124                         if (parentNode.left.value == value) {
    125                             parentNode.left = targetNode.left;
    126                         } else {// 待删除节点是父节点的右子节点
    127                             parentNode.right = targetNode.left;
    128                         }
    129                     } else {
    130                         root = targetNode.left;
    131                     }
    132                 } else {// 如果要删除的节点有右子节点
    133                     if (parentNode != null) {
    134                         // 待删除节点是父节点的左子节点
    135                         if (parentNode.left.value == value) {
    136                             parentNode.left = targetNode.right;
    137                         } else {// 待删除节点是父节点的右子节点
    138                             parentNode.right = targetNode.right;
    139                         }
    140                     } else {
    141                         root = targetNode.right;
    142                     }
    143                 }
    144             }
    145         }
    146     }
    147 
    148     /**
    149      * 添加节点的方法
    150      *
    151      * @param node
    152      */
    153     public void addNode(Node node) {
    154         // root节点为空,就让root成为根节点
    155         if (root == null) {
    156             root = node;
    157         } else {// root节点不为空,就继续向树中添加节点
    158             root.addNode(node);
    159         }
    160     }
    161 
    162     /**
    163      * 进行中序遍历
    164      */
    165     public void infixOrder() {
    166         if (root != null) {
    167             root.infixOrder();
    168         } else {
    169             System.out.println("二叉树为空,无法进行排序!");
    170         }
    171     }
    172 }
    173 
    174 class Node {
    175     int value;
    176     Node left;
    177     Node right;
    178 
    179     public Node(int value) {
    180         this.value = value;
    181     }
    182 
    183     @Override
    184     public String toString() {
    185         return "Node{" +
    186                 "value=" + value +
    187                 '}';
    188     }
    189 
    190     /**
    191      * 返回以当前节点为根节点的树的高度
    192      *
    193      * @return 返回树的高度
    194      */
    195     public int heightTree() {
    196         // 比较左子树跟右子树的高度,返回最大的。+1是因为树本身还要站一层
    197         return Math.max(left == null ? 0 : left.heightTree(), right == null ? 0 : right.heightTree()) + 1;
    198     }
    199 
    200     /**
    201      * 返回左子树的高度
    202      *
    203      * @return 左子树高度
    204      */
    205     public int leftHeight() {
    206         if (left == null) {
    207             return 0;
    208         }
    209         return left.heightTree();
    210     }
    211 
    212     /**
    213      * 返回右子树的高度
    214      *
    215      * @return 右子树高度
    216      */
    217     public int rightHeight() {
    218         if (right == null) {
    219             return 0;
    220         }
    221         return right.heightTree();
    222     }
    223 
    224     /**
    225      * 查找到要删除的节点
    226      *
    227      * @param value 希望删除节点的值
    228      * @return 找到了就返回这个要删除的节点,没有找到就返回null
    229      */
    230     public Node delSearch(int value) {
    231         // 找到的就是要删除的节点
    232         if (value == this.value) {
    233             return this;
    234         } else if (value < this.value) {
    235             /**向左子节点查找*/
    236             if (this.left == null) {
    237                 return null;
    238             }
    239             // 继续递归查找
    240             return this.left.delSearch(value);
    241         } else {// 要删除节点的值是大于等于当前节点的值
    242             if (this.right == null) {
    243                 return null;
    244             }
    245             return this.right.delSearch(value);
    246         }
    247     }
    248 
    249     /**
    250      * 查找到要删除节点的父节点
    251      *
    252      * @param value 要删除的节点的值
    253      * @return 找到就返回要删除节点的父节点,没有找到就返回null
    254      */
    255     public Node delSearchParent(int value) {
    256         // 如果当前节点就是要删除的节点的父节点,就返回
    257         if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
    258             return this;
    259         } else {
    260             // 满足条件表示向左递归查找
    261             if (this.left != null && value < this.value) {
    262                 // 向左子树递归找到就返回
    263                 return this.left.delSearchParent(value);
    264             } else if (this.right != null && value >= this.value) {
    265                 // 向右子树递归找到就返回
    266                 return this.right.delSearchParent(value);
    267             } else {
    268                 // 没有找到要删除节点的父节点
    269                 return null;
    270             }
    271         }
    272     }
    273 
    274     /**
    275      * 二叉排序树的添加
    276      *
    277      * @param node
    278      */
    279     public void addNode(Node node) {
    280         if (node == null) {
    281             return;
    282         }
    283         // 判断传进来的节点值与当前节点的关系
    284         if (node.value < this.value) {
    285             // 左子节点为null
    286             if (this.left == null) {
    287                 this.left = node;
    288             } else {
    289                 // 递归的向左子树添加节点
    290                 this.left.addNode(node);
    291             }
    292         } else { // 传入的节点大于等于当前节点
    293             if (this.right == null) {
    294                 this.right = node;
    295             } else {
    296                 // 递归的向右子树添加节点
    297                 this.right.addNode(node);
    298             }
    299         }
    300 
    301     }
    302 
    303     /**
    304      * 二叉排序树的中序遍历
    305      */
    306     void infixOrder() {
    307         if (this.left != null) {
    308             this.left.infixOrder();
    309         }
    310         System.out.println(this);
    311         if (this.right != null) {
    312             this.right.infixOrder();
    313         }
    314     }
    315 }
    View Code

    AVL左旋代码:  

    在Node节点中添加AVL树左旋的方法

    /**
     * AVL树左旋转的方法
     */
    public void leftHand() {
        // 1.创建新的节点,值为当前节点的值
        Node newNode = new Node(this.value);
        // 2.把新节点的左子树设置为原节点的左子树
        newNode.left = this.left;
        // 3.把新节点的右子树设为当前节点的右子树的左子树
        newNode.right = this.right.left;
        // 4.把当前节点的值换成当前节点右子节点的值
        this.value = this.right.value;
        // 5.把当前节点的右子树设为右子树的右子树
        this.right = this.right.right;
        // 6.把当前节点的左子树设置为新的节点
        this.left = newNode;
    }
    

    在Node节点中添加树的方法中加入左旋判断

     1 /**
     2  * 二叉排序树的添加
     3  *
     4  * @param node
     5  */
     6 public void addNode(Node node) {
     7     if (node == null) {
     8         return;
     9     }
    10     // 判断传进来的节点值与当前节点的关系
    11     if (node.value < this.value) {
    12         // 左子节点为null
    13         if (this.left == null) {
    14             this.left = node;
    15         } else {
    16             // 递归的向左子树添加节点
    17             this.left.addNode(node);
    18         }
    19     } else { // 传入的节点大于等于当前节点
    20         if (this.right == null) {
    21             this.right = node;
    22         } else {
    23             // 递归的向右子树添加节点
    24             this.right.addNode(node);
    25         }
    26     }
    27     // 当添加完成一个节点后:(右子树的高度-左子树的高度)>1,进行左旋操作
    28     if (rightHeight() - leftHeight() > 1) {
    29         // 进行左旋操作
    30         leftHand();
    31     }
    32 }

    测试:

    public static void main(String[] args) {
        int[] arr = {4, 3, 6, 5, 7, 8};
        // 创建AVL树对象
        AVLTree avlTree = new AVLTree();
        for (int i = 0; i < arr.length; i++) {
            // 添加节点
            avlTree.addNode(new Node(arr[i]));
        }
        // 中序遍历
        System.out.println("======================中序遍历======================");
        avlTree.infixOrder();
        System.out.println("======================AVL树左旋平衡处理======================");
        System.out.println("左旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
        System.out.println("左旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
        System.out.println("左旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());
    }
    

     AVL树右旋

    AVL树右旋图解

    AVL树右旋代码:

     在Node类中添加平衡二叉树右旋代码:

     /**
      * AVL树右旋转的方法
      */
     public void rightHand() {
         // 1.创建新的节点,值为当前节点的值
         Node newNode = new Node(this.value);
         // 2.把新节点的右子树设置为当前节点的右子树
         newNode.right = this.right;
         // 3.把新节点的左子树设为当前节点的左子树的右子树
         newNode.left = this.left.right;
         // 4.把当前节点的值换成当前节点左子节点的值
         this.value = this.left.value;
         // 5.把当前节点的左子树设为左子树的左子树
         this.left = this.left.left;
         // 6.把当前节点的右子树设置为新的节点
         this.right = newNode;
     }
    

    在Node类中添加节点时候增加判断判断节点应该左旋还是右旋:

     1 /**
     2  * 二叉排序树的添加
     3  *
     4  * @param node
     5  */
     6 public void addNode(Node node) {
     7     if (node == null) {
     8         return;
     9     }
    10     // 判断传进来的节点值与当前节点的关系
    11     if (node.value < this.value) {
    12         // 左子节点为null
    13         if (this.left == null) {
    14             this.left = node;
    15         } else {
    16             // 递归的向左子树添加节点
    17             this.left.addNode(node);
    18         }
    19     } else { // 传入的节点大于等于当前节点
    20         if (this.right == null) {
    21             this.right = node;
    22         } else {
    23             // 递归的向右子树添加节点
    24             this.right.addNode(node);
    25         }
    26     }
    27     // 当添加完成一个节点后:(右子树的高度-左子树的高度)>1,进行左旋操作
    28     if (rightHeight() - leftHeight() > 1) {
    29         // 进行左旋操作
    30         leftHand();
    31     }
    32     // 当添加完成一个节点后:(左子树的高度-右子树的高度)>1,进行右旋操作
    33     if (leftHeight() - rightHeight() > 1) {
    34         // 进行左旋操作
    35         rightHand();
    36     }
    37 }

    测试:

    public static void main(String[] args) {
        // 测试左旋转
        //int[] arr = {4, 3, 6, 5, 7, 8};
        // 测试右旋转
        int[] arr = {10, 12, 8, 9, 7, 6};
        // 创建AVL树对象
        AVLTree avlTree = new AVLTree();
        for (int i = 0; i < arr.length; i++) {
            // 添加节点
            avlTree.addNode(new Node(arr[i]));
        }
        // 中序遍历
        /*System.out.println("======================中序遍历======================");
        avlTree.infixOrder();
        System.out.println("======================AVL树左旋平衡处理======================");
        System.out.println("左旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
        System.out.println("左旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
        System.out.println("左旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());*/
        System.out.println("======================中序遍历======================");
        avlTree.infixOrder();
        System.out.println("======================AVL树右旋平衡处理======================");
        System.out.println("右旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
        System.out.println("右旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
        System.out.println("右旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());
        System.out.println("右旋平衡处理后树的根节点的值:"+avlTree.getRoot().value);
    }
    

    AVL树双旋

    AVL树双旋问题以及图解:

    右旋遇到的问题:  

      前面的两个数列,进行单旋转(即一次旋转)就可以将非平衡二叉树转成平衡二叉树,但是在某些情况下,单旋转不能完成平衡二叉树的转换。

    比如数列int[] arr = { 10, 11, 7, 6, 8, 9 };   运行原来的代码可以看到,并没有转成 AVL 树.  

    public static void main(String[] args) {
            // 测试左旋转
            //int[] arr = {4, 3, 6, 5, 7, 8};
    
            // 测试右旋转
            //int[] arr = {10, 12, 8, 9, 7, 6};
    
            // 测试旋转
            int[] arr = { 10, 11, 7, 6, 8, 9 };
    
            // 创建AVL树对象
            AVLTree avlTree = new AVLTree();
    
            for (int i = 0; i < arr.length; i++) {
                // 添加节点
                avlTree.addNode(new Node(arr[i]));
            }
    
            // 中序遍历
            /*System.out.println("======================中序遍历======================");
            avlTree.infixOrder();
            System.out.println("======================AVL树左旋平衡处理======================");
            System.out.println("左旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
            System.out.println("左旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
            System.out.println("左旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());*/
    
            System.out.println("======================中序遍历======================");
            avlTree.infixOrder();
            System.out.println("======================AVL树右旋平衡处理======================");
            System.out.println("右旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
            System.out.println("右旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
            System.out.println("右旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());
            System.out.println("右旋平衡处理后树的根节点的值:" + avlTree.getRoot().value);
        }
    

    可以发现,将数列进行二叉排序树后的右旋处理,并没有变成AVL数:(3 - 1) > 1 所以说不满足AVL树   

    解决思路:

    1. 当符号右旋转的条件时
    2. 如果它的左子树的右子树高度大于它的左子树的高度
    3. 先对当前这个结点的左节点进行左旋转
    4. 在对当前结点进行右旋转的操作即可

     代码实现:

    在添加节点的时候增加判断,直接上代码了:

    /**
     * 二叉排序树的添加
     *
     * @param node
     */
    public void addNode(Node node) {
        if (node == null) {
            return;
        }
        // 判断传进来的节点值与当前节点的关系
        if (node.value < this.value) {
            // 左子节点为null
            if (this.left == null) {
                this.left = node;
            } else {
                // 递归的向左子树添加节点
                this.left.addNode(node);
            }
        } else { // 传入的节点大于等于当前节点
            if (this.right == null) {
                this.right = node;
            } else {
                // 递归的向右子树添加节点
                this.right.addNode(node);
            }
        }
        // 当添加完成一个节点后:(右子树的高度-左子树的高度)>1,进行左旋操作
        if (rightHeight() - leftHeight() > 1) {
            // 左旋之双旋判断
            if (this.right != null && this.right.rightHeight() > this.right.rightHeight()) {
                // 先对右子结点进行右旋转
                this.right.rightHand();
                // 再进行左旋操作
                leftHand();
            } else {
                // 直接进行左旋操作
                leftHand();
            }
            // 右旋判断: 当添加完成一个节点后:(左子树的高度-右子树的高度)>1,进行右旋操作
        } else if (leftHeight() - rightHeight() > 1) {
            // 右旋之双旋判断
            if (this.left != null && this.left.rightHeight() > this.left.leftHeight()) {
                // 先对当前结点的左结点(左子树)->左旋转
                this.left.leftHand();
                // 再进行右旋操作
                rightHand();
            } else {
                // 直接进行右旋操作
                rightHand();
            }
        }
    }

    测试:

    总体代码:

    package Demo11_平衡二叉树_AVL树;
    
    /**
     * @author zhangzhixi
     * @date 2021/3/12 22:58
     */
    public class AVLTreeDemo {
        public static void main(String[] args) {
            // 测试左旋转
            //int[] arr = {4, 3, 6, 5, 7, 8};
    
            // 测试右旋转
            //int[] arr = {10, 12, 8, 9, 7, 6};
    
            // 测试旋转
            int[] arr = {10, 11, 7, 6, 8, 9};
    
            // 创建AVL树对象
            AVLTree avlTree = new AVLTree();
    
            for (int i = 0; i < arr.length; i++) {
                // 添加节点
                avlTree.addNode(new Node(arr[i]));
            }
    
            // 中序遍历
            /*System.out.println("======================中序遍历======================");
            avlTree.infixOrder();
            System.out.println("======================AVL树左旋平衡处理======================");
            System.out.println("左旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
            System.out.println("左旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
            System.out.println("左旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());*/
    
            System.out.println("======================中序遍历======================");
            avlTree.infixOrder();
            System.out.println("======================AVL树右旋平衡处理======================");
            System.out.println("右旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
            System.out.println("右旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
            System.out.println("右旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());
            System.out.println("右旋平衡处理后树的根节点的值:" + avlTree.getRoot().value);
        }
    }
    
    class AVLTree {
        private Node root;
    
        public Node getRoot() {
            return root;
        }
    
        /**
         * 查找要删除的节点
         *
         * @param value
         * @return
         */
        public Node delSearch(int value) {
            if (root == null) {
                return null;
            } else {
                return root.delSearch(value);
            }
        }
    
        /**
         * 查找到要删除节点的父节点
         *
         * @param value
         * @return
         */
        public Node delSearchParent(int value) {
            if (root == null) {
                return null;
            } else {
                return root.delSearchParent(value);
            }
        }
    
        /**
         * 找到最小值并删除
         *
         * @param node
         * @return 返回删除节点的值
         */
        public int delRightTreeMin(Node node) {
            // 作一个辅助节点
            Node target = node;
            // 循环往左子树进行查找,就会找到最小值
            while (target.left != null) {
                target = target.left;
            }
            // 删除最小值
            delNode(target.value);
            // 返回最小值
            return target.value;
        }
    
        /**
         * 删除节点
         *
         * @param value
         */
        public void delNode(int value) {
            if (root == null) {
                return;
            } else {
                // 1、找到要删除的节点
                Node targetNode = delSearch(value);
                // 没有找到
                if (targetNode == null) {
                    return;
                }
                // 表示这颗二叉排序树只有一个节点(父节点)
                if (root.left == null && root.right == null) {
                    root = null;
                    return;
                }
    
                // 2、找到要删除节点的父节点
                Node parentNode = delSearchParent(value);
                // 表示要删除的节点是一个叶子节点
                if (targetNode.left == null && targetNode.right == null) {
                    // 继续判断这个叶子节点是父节点的左子节点还是右子节点
                    if (parentNode.left != null && parentNode.left.value == value) {
                        // 将这个叶子节点置为空
                        parentNode.left = null;
                    } else if (parentNode.right != null && parentNode.right.value == value) {
                        parentNode.right = null;
                    }
                } else if (targetNode.left != null && targetNode.right != null) {// 删除有两颗子树的节点
                    // 找到最小值
                    int minVal = delRightTreeMin(targetNode.right);
                    // 重置
                    targetNode.value = minVal;
                } else {// 删除只有一颗子树的节点
                    if (targetNode.left != null) {// 如果要删除的节点有左子节点
                        if (parentNode != null) {
                            // 待删除节点是父节点的左子节点
                            if (parentNode.left.value == value) {
                                parentNode.left = targetNode.left;
                            } else {// 待删除节点是父节点的右子节点
                                parentNode.right = targetNode.left;
                            }
                        } else {
                            root = targetNode.left;
                        }
                    } else {// 如果要删除的节点有右子节点
                        if (parentNode != null) {
                            // 待删除节点是父节点的左子节点
                            if (parentNode.left.value == value) {
                                parentNode.left = targetNode.right;
                            } else {// 待删除节点是父节点的右子节点
                                parentNode.right = targetNode.right;
                            }
                        } else {
                            root = targetNode.right;
                        }
                    }
                }
            }
        }
    
        /**
         * 添加节点的方法
         *
         * @param node
         */
        public void addNode(Node node) {
            // root节点为空,就让root成为根节点
            if (root == null) {
                root = node;
            } else {// root节点不为空,就继续向树中添加节点
                root.addNode(node);
            }
        }
    
        /**
         * 进行中序遍历
         */
        public void infixOrder() {
            if (root != null) {
                root.infixOrder();
            } else {
                System.out.println("二叉树为空,无法进行排序!");
            }
        }
    }
    
    class Node {
        int value;
        Node left;
        Node right;
    
        public Node(int value) {
            this.value = value;
        }
    
        @Override
        public String toString() {
            return "Node{" +
                    "value=" + value +
                    '}';
        }
    
        /**
         * 返回以当前节点为根节点的树的高度
         *
         * @return 返回树的高度
         */
        public int heightTree() {
            // 比较左子树跟右子树的高度,返回最大的。+1是因为树本身还要站一层
            return Math.max(left == null ? 0 : left.heightTree(), right == null ? 0 : right.heightTree()) + 1;
        }
    
        /**
         * 返回左子树的高度
         *
         * @return 左子树高度
         */
        public int leftHeight() {
            if (left == null) {
                return 0;
            }
            return left.heightTree();
        }
    
        /**
         * 返回右子树的高度
         *
         * @return 右子树高度
         */
        public int rightHeight() {
            if (right == null) {
                return 0;
            }
            return right.heightTree();
        }
    
        /**
         * 查找到要删除的节点
         *
         * @param value 希望删除节点的值
         * @return 找到了就返回这个要删除的节点,没有找到就返回null
         */
        public Node delSearch(int value) {
            // 找到的就是要删除的节点
            if (value == this.value) {
                return this;
            } else if (value < this.value) {
                /**向左子节点查找*/
                if (this.left == null) {
                    return null;
                }
                // 继续递归查找
                return this.left.delSearch(value);
            } else {// 要删除节点的值是大于等于当前节点的值
                if (this.right == null) {
                    return null;
                }
                return this.right.delSearch(value);
            }
        }
    
        /**
         * 查找到要删除节点的父节点
         *
         * @param value 要删除的节点的值
         * @return 找到就返回要删除节点的父节点,没有找到就返回null
         */
        public Node delSearchParent(int value) {
            // 如果当前节点就是要删除的节点的父节点,就返回
            if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
                return this;
            } else {
                // 满足条件表示向左递归查找
                if (this.left != null && value < this.value) {
                    // 向左子树递归找到就返回
                    return this.left.delSearchParent(value);
                } else if (this.right != null && value >= this.value) {
                    // 向右子树递归找到就返回
                    return this.right.delSearchParent(value);
                } else {
                    // 没有找到要删除节点的父节点
                    return null;
                }
            }
        }
    
        /**
         * 二叉排序树的添加
         *
         * @param node
         */
        public void addNode(Node node) {
            if (node == null) {
                return;
            }
            // 判断传进来的节点值与当前节点的关系
            if (node.value < this.value) {
                // 左子节点为null
                if (this.left == null) {
                    this.left = node;
                } else {
                    // 递归的向左子树添加节点
                    this.left.addNode(node);
                }
            } else { // 传入的节点大于等于当前节点
                if (this.right == null) {
                    this.right = node;
                } else {
                    // 递归的向右子树添加节点
                    this.right.addNode(node);
                }
            }
            // 当添加完成一个节点后:(右子树的高度-左子树的高度)>1,进行左旋操作
            if (rightHeight() - leftHeight() > 1) {
                // 左旋之双旋判断
                if (this.right != null && this.right.rightHeight() > this.right.rightHeight()) {
                    // 先对右子结点进行右旋转
                    this.right.rightHand();
                    // 再进行左旋操作
                    leftHand();
                } else {
                    // 直接进行左旋操作
                    leftHand();
                }
                // 右旋判断: 当添加完成一个节点后:(左子树的高度-右子树的高度)>1,进行右旋操作
            } else if (leftHeight() - rightHeight() > 1) {
                // 右旋之双旋判断
                if (this.left != null && this.left.rightHeight() > this.left.leftHeight()) {
                    // 先对当前结点的左结点(左子树)->左旋转
                    this.left.leftHand();
                    // 再进行右旋操作
                    rightHand();
                } else {
                    // 直接进行右旋操作
                    rightHand();
                }
            }
        }
    
        /**
         * 二叉排序树的中序遍历
         */
        void infixOrder() {
            if (this.left != null) {
                this.left.infixOrder();
            }
            System.out.println(this);
            if (this.right != null) {
                this.right.infixOrder();
            }
        }
    
        /**
         * AVL树左旋转的方法
         */
        public void leftHand() {
            // 1.创建新的节点,值为当前节点的值
            Node newNode = new Node(this.value);
            // 2.把新节点的左子树设置为当前节点的左子树
            newNode.left = this.left;
            // 3.把新节点的右子树设为当前节点的右子树的左子树
            newNode.right = this.right.left;
            // 4.把当前节点的值换成当前节点右子节点的值
            this.value = this.right.value;
            // 5.把当前节点的右子树设为右子树的右子树
            this.right = this.right.right;
            // 6.把当前节点的左子树设置为新的节点
            this.left = newNode;
        }
    
        /**
         * AVL树右旋转的方法
         */
        public void rightHand() {
            // 1.创建新的节点,值为当前节点的值
            Node newNode = new Node(this.value);
            // 2.把新节点的右子树设置为当前节点的右子树
            newNode.right = this.right;
            // 3.把新节点的左子树设为当前节点的左子树的右子树
            newNode.left = this.left.right;
            // 4.把当前节点的值换成当前节点左子节点的值
            this.value = this.left.value;
            // 5.把当前节点的左子树设为左子树的左子树
            this.left = this.left.left;
            // 6.把当前节点的右子树设置为新的节点
            this.right = newNode;
        }
    }
    

     

  • 相关阅读:
    JavaScript 简介
    HTML 标签列表
    HTML5 是什么
    初识HTML5
    Conda 环境增删改查导出导入
    Windows 下安装 CGAL 并验证安装
    3D 点云数据集整理分析
    内网穿透
    SSH 免密登录
    串口、COM口、TTL、RS-232、RS-485区别详解
  • 原文地址:https://www.cnblogs.com/zhangzhixi/p/14526892.html
Copyright © 2020-2023  润新知