zoukankan      html  css  js  c++  java
  • D13-平衡二叉树[Java数据结构和算法]

    1.平衡二叉树基本介绍

      1.1平衡二叉树又叫平衡二叉搜索树(Selg-balancing binary search tree),又叫AVL树,可以保证查询效率较高;

      1.2 平衡二叉树是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是平衡二叉树。

      1.3 平衡二叉树的常用实现方法有红黑树,AVL,替罪羊树,Treap,伸展树等;

     旋转详见

    2. AVL左旋转

      2.1 左旋转流程(右子树的高度高)

      2.2 右旋转流程(左子树的高度高)

      2.3 双旋转,存在以下的情况不能单单进行一方旋转

      2.4 代码实现

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

      

  • 相关阅读:
    无序数组求第K大/第K小的数
    [洛谷][二分搜索]进击的奶牛
    [015]向下类型转换和向上类型转换
    [014]析构函数为虚函数的注意事项
    [013]函数重载--int*和void*的匹配优先级
    [012]链表笔记--在链表中插入一个节点
    [011]链表笔记--删除一个链表节点
    [002]链表笔记--编程实现一个单链表的创建/测长/打印
    [C++]对象的销毁机制
    [011]默认实参
  • 原文地址:https://www.cnblogs.com/ERFishing/p/11377523.html
Copyright © 2011-2022 走看看