zoukankan      html  css  js  c++  java
  • 平衡二叉树(AVL树)

    一、基本介绍

      1)平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树, 可以保证查询效率较高。
      2)具有以下特点:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

      3)平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。

    二、左旋

      1)已知数列{4,3,6,5,7,8}

       

     1     //左旋转方法
     2     private void leftRotate() {
     3         
     4         //创建新的结点,以当前根结点的值
     5         Node newNode = new Node(value);
     6         //把新的结点的左子树设置成当前结点的左子树
     7         newNode.left = left;
     8         //把新的结点的右子树设置成带你过去结点的右子树的左子树
     9         newNode.right = right.left;
    10         //把当前结点的值替换成右子结点的值
    11         value = right.value;
    12         //把当前结点的右子树设置成当前结点右子树的右子树
    13         right = right.right;
    14         //把当前结点的左子树(左子结点)设置成新的结点
    15         left = newNode;                
    16     }

    三、右旋

      1)已知数列{10,12,8,9,7,6}

      

    1     //右旋转
    2     private void rightRotate() {
    3         Node newNode = new Node(value);
    4         newNode.right = right;
    5         newNode.left = left.right;
    6         value = left.value;
    7         left = left.left;
    8         right = newNode;
    9     }

    四、双旋

      前面的两个数列,进行单旋转(即一次旋转)就可以将非平衡二叉树转成平衡二叉树,但是在某些情况下,单旋转不能完成平衡二叉树的转换。比如数列
      int[] arr = { 10, 11, 7, 6, 8, 9 };  运行原来的代码可以看到,并没有转成 AVL树.
      int[] arr = {2,1,6,5,7,3}; // 运行原来的代码可以看到,并没有转成 AVL树

      

    五、完整代码

      

      1 public class AVLTreeDemo {
      2 
      3     public static void main(String[] args) {
      4         //int[] arr = {4,3,6,5,7,8};
      5         //int[] arr = { 10, 12, 8, 9, 7, 6 };
      6         int[] arr = { 10, 11, 7, 6, 8, 9 };  
      7         //创建一个 AVLTree对象
      8         AVLTree avlTree = new AVLTree();
      9         //添加结点
     10         for(int i=0; i < arr.length; i++) {
     11             avlTree.add(new Node(arr[i]));
     12         }
     13         
     14         //遍历
     15         System.out.println("中序遍历");
     16         avlTree.infixOrder();
     17         
     18         System.out.println("在平衡处理~~");
     19         System.out.println("树的高度=" + avlTree.getRoot().height()); //3
     20         System.out.println("树的左子树高度=" + avlTree.getRoot().leftHeight()); // 2
     21         System.out.println("树的右子树高度=" + avlTree.getRoot().rightHeight()); // 2
     22         System.out.println("当前的根结点=" + avlTree.getRoot());//8
     23         
     24         
     25     }
     26 
     27 }
     28 
     29 // 创建AVLTree
     30 class AVLTree {
     31     private Node root;
     32 
     33     public Node getRoot() {
     34         return root;
     35     }
     36 
     37     // 查找要删除的结点
     38     public Node search(int value) {
     39         if (root == null) {
     40             return null;
     41         } else {
     42             return root.search(value);
     43         }
     44     }
     45 
     46     // 查找父结点
     47     public Node searchParent(int value) {
     48         if (root == null) {
     49             return null;
     50         } else {
     51             return root.searchParent(value);
     52         }
     53     }
     54 
     55     // 编写方法:
     56     // 1. 返回的 以node 为根结点的二叉排序树的最小结点的值
     57     // 2. 删除node 为根结点的二叉排序树的最小结点
     58     /**
     59      * 
     60      * @param node
     61      *            传入的结点(当做二叉排序树的根结点)
     62      * @return 返回的 以node 为根结点的二叉排序树的最小结点的值
     63      */
     64     public int delRightTreeMin(Node node) {
     65         Node target = node;
     66         // 循环的查找左子节点,就会找到最小值
     67         while (target.left != null) {
     68             target = target.left;
     69         }
     70         // 这时 target就指向了最小结点
     71         // 删除最小结点
     72         delNode(target.value);
     73         return target.value;
     74     }
     75 
     76     // 删除结点
     77     public void delNode(int value) {
     78         if (root == null) {
     79             return;
     80         } else {
     81             // 1.需求先去找到要删除的结点 targetNode
     82             Node targetNode = search(value);
     83             // 如果没有找到要删除的结点
     84             if (targetNode == null) {
     85                 return;
     86             }
     87             // 如果我们发现当前这颗二叉排序树只有一个结点
     88             if (root.left == null && root.right == null) {
     89                 root = null;
     90                 return;
     91             }
     92 
     93             // 去找到targetNode的父结点
     94             Node parent = searchParent(value);
     95             // 如果要删除的结点是叶子结点
     96             if (targetNode.left == null && targetNode.right == null) {
     97                 // 判断targetNode 是父结点的左子结点,还是右子结点
     98                 if (parent.left != null && parent.left.value == value) { // 是左子结点
     99                     parent.left = null;
    100                 } else if (parent.right != null && parent.right.value == value) {// 是由子结点
    101                     parent.right = null;
    102                 }
    103             } else if (targetNode.left != null && targetNode.right != null) { // 删除有两颗子树的节点
    104                 int minVal = delRightTreeMin(targetNode.right);
    105                 targetNode.value = minVal;
    106 
    107             } else { // 删除只有一颗子树的结点
    108                 // 如果要删除的结点有左子结点
    109                 if (targetNode.left != null) {
    110                     if (parent != null) {
    111                         // 如果 targetNode 是 parent 的左子结点
    112                         if (parent.left.value == value) {
    113                             parent.left = targetNode.left;
    114                         } else { // targetNode 是 parent 的右子结点
    115                             parent.right = targetNode.left;
    116                         }
    117                     } else {
    118                         root = targetNode.left;
    119                     }
    120                 } else { // 如果要删除的结点有右子结点
    121                     if (parent != null) {
    122                         // 如果 targetNode 是 parent 的左子结点
    123                         if (parent.left.value == value) {
    124                             parent.left = targetNode.right;
    125                         } else { // 如果 targetNode 是 parent 的右子结点
    126                             parent.right = targetNode.right;
    127                         }
    128                     } else {
    129                         root = targetNode.right;
    130                     }
    131                 }
    132 
    133             }
    134 
    135         }
    136     }
    137 
    138     // 添加结点的方法
    139     public void add(Node node) {
    140         if (root == null) {
    141             root = node;// 如果root为空则直接让root指向node
    142         } else {
    143             root.add(node);
    144         }
    145     }
    146 
    147     // 中序遍历
    148     public void infixOrder() {
    149         if (root != null) {
    150             root.infixOrder();
    151         } else {
    152             System.out.println("二叉排序树为空,不能遍历");
    153         }
    154     }
    155 }
    156 
    157 // 创建Node结点
    158 class Node {
    159     int value;
    160     Node left;
    161     Node right;
    162 
    163     public Node(int value) {
    164 
    165         this.value = value;
    166     }
    167 
    168     // 返回左子树的高度
    169     public int leftHeight() {
    170         if (left == null) {
    171             return 0;
    172         }
    173         return left.height();
    174     }
    175 
    176     // 返回右子树的高度
    177     public int rightHeight() {
    178         if (right == null) {
    179             return 0;
    180         }
    181         return right.height();
    182     }
    183 
    184     // 返回 以该结点为根结点的树的高度
    185     public int height() {
    186         return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
    187     }
    188     
    189     //左旋转方法
    190     private void leftRotate() {
    191         
    192         //创建新的结点,以当前根结点的值
    193         Node newNode = new Node(value);
    194         //把新的结点的左子树设置成当前结点的左子树
    195         newNode.left = left;
    196         //把新的结点的右子树设置成带你过去结点的右子树的左子树
    197         newNode.right = right.left;
    198         //把当前结点的值替换成右子结点的值
    199         value = right.value;
    200         //把当前结点的右子树设置成当前结点右子树的右子树
    201         right = right.right;
    202         //把当前结点的左子树(左子结点)设置成新的结点
    203         left = newNode;                
    204     }
    205     
    206     //右旋转
    207     private void rightRotate() {
    208         Node newNode = new Node(value);
    209         newNode.right = right;
    210         newNode.left = left.right;
    211         value = left.value;
    212         left = left.left;
    213         right = newNode;
    214     }
    215 
    216     // 查找要删除的结点
    217     /**
    218      * 
    219      * @param value
    220      *            希望删除的结点的值
    221      * @return 如果找到返回该结点,否则返回null
    222      */
    223     public Node search(int value) {
    224         if (value == this.value) { // 找到就是该结点
    225             return this;
    226         } else if (value < this.value) {// 如果查找的值小于当前结点,向左子树递归查找
    227             // 如果左子结点为空
    228             if (this.left == null) {
    229                 return null;
    230             }
    231             return this.left.search(value);
    232         } else { // 如果查找的值不小于当前结点,向右子树递归查找
    233             if (this.right == null) {
    234                 return null;
    235             }
    236             return this.right.search(value);
    237         }
    238 
    239     }
    240 
    241     // 查找要删除结点的父结点
    242     /**
    243      * 
    244      * @param value
    245      *            要找到的结点的值
    246      * @return 返回的是要删除的结点的父结点,如果没有就返回null
    247      */
    248     public Node searchParent(int value) {
    249         // 如果当前结点就是要删除的结点的父结点,就返回
    250         if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
    251             return this;
    252         } else {
    253             // 如果查找的值小于当前结点的值, 并且当前结点的左子结点不为空
    254             if (value < this.value && this.left != null) {
    255                 return this.left.searchParent(value); // 向左子树递归查找
    256             } else if (value >= this.value && this.right != null) {
    257                 return this.right.searchParent(value); // 向右子树递归查找
    258             } else {
    259                 return null; // 没有找到父结点
    260             }
    261         }
    262 
    263     }
    264 
    265     @Override
    266     public String toString() {
    267         return "Node [value=" + value + "]";
    268     }
    269 
    270     // 添加结点的方法
    271     // 递归的形式添加结点,注意需要满足二叉排序树的要求
    272     public void add(Node node) {
    273         if (node == null) {
    274             return;
    275         }
    276 
    277         // 判断传入的结点的值,和当前子树的根结点的值关系
    278         if (node.value < this.value) {
    279             // 如果当前结点左子结点为null
    280             if (this.left == null) {
    281                 this.left = node;
    282             } else {
    283                 // 递归的向左子树添加
    284                 this.left.add(node);
    285             }
    286         } else { // 添加的结点的值大于 当前结点的值
    287             if (this.right == null) {
    288                 this.right = node;
    289             } else {
    290                 // 递归的向右子树添加
    291                 this.right.add(node);
    292             }
    293 
    294         }
    295         
    296         //当添加完一个结点后,如果: (右子树的高度-左子树的高度) > 1 , 左旋转
    297         if(rightHeight() - leftHeight() > 1) {
    298             //如果它的右子树的左子树的高度大于它的右子树的右子树的高度
    299             if(right != null && right.leftHeight() > right.rightHeight()) {
    300                 //先对右子结点进行右旋转
    301                 right.rightRotate();
    302                 //然后在对当前结点进行左旋转
    303                 leftRotate(); //左旋转..
    304             } else {
    305                 //直接进行左旋转即可
    306                 leftRotate();
    307             }
    308             return ; //必须要!!!
    309         }
    310         
    311         //当添加完一个结点后,如果 (左子树的高度 - 右子树的高度) > 1, 右旋转
    312         if(leftHeight() - rightHeight() > 1) {
    313             //如果它的左子树的右子树高度大于它的左子树的高度
    314             if(left != null && left.rightHeight() > left.leftHeight()) {
    315                 //先对当前结点的左结点(左子树)->左旋转
    316                 left.leftRotate();
    317                 //再对当前结点进行右旋转
    318                 rightRotate();
    319             } else {
    320                 //直接进行右旋转即可
    321                 rightRotate();
    322             }
    323         }
    324     }
    325 
    326     // 中序遍历
    327     public void infixOrder() {
    328         if (this.left != null) {
    329             this.left.infixOrder();
    330         }
    331         System.out.println(this);
    332         if (this.right != null) {
    333             this.right.infixOrder();
    334         }
    335     }
    336 
    337 }
  • 相关阅读:
    UICollectionView中使用 UICollectionViewFlowLayout进行布局(模仿苹果相册)
    使用CocoaPods被卡住:Updating local specs repositories
    【原】iOS 同时重写setter和getter时候报错:Use of undeclared identifier '_name';did you mean 'name'
    iOS 设置不同的字体颜色
    使用java代码,动态给TextView设置drawable
    格式化浮点数(保留指定位数)
    监听输入法的出现和隐藏
    dp和px的转换
    获取状态栏高度
    获取在attr.xml中声明的主题样式
  • 原文地址:https://www.cnblogs.com/hyunbar/p/11508831.html
Copyright © 2011-2022 走看看