平衡二叉树(Balanced Binary Tree)

平衡二叉树(Balanced Binary Tree)又称AVL树。AVL 树得名于它的发明者 G. M. Adelson-Velsky 和 Evgenii Landis,他们在1962年的论文《An algorithm for the organization of information》中公开了这一数据结构。这种结构完成一系列集合操作时,时间复杂度与空间复杂度比其他树要低,一系列操作始终保持平衡!为大型数据库的组织、索引提供了一条全新的的道路!

1、BST树的不足

现给定一组有序数列[1, 2, 3, 4, 5, 6],要求创建一颗二叉排序树(BST),并分析问题所在!

由数列[1, 2, 3, 4, 5, 6]构成的BST树,如上图所示。那么这棵树存在的问题有以下几点:

1、左子树全部为空,从形式上看,更像一个单链表,实际上已经退化成一个单链表了

2、对于插入、删除、修改速度是没有影响,但是这种结构查询效率会明显降低(因为需要依次比较)不能发挥BST的优势,因为每次还需要比较左子树,器查询速度比单链表还慢。

因此为了解决二叉排序树(BST)退化成链表的问题,出现了一种全新的树结构(AVL树),这种结构不仅能防止退化成单链表,还在插入,查找,删除得到了进一步的改善,所使用的时间复杂度和空间复杂度相较于其他树更低!

2、AVL树介绍

1、平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为 AVL 树,可以保证查询效率较高。

2、具有以下特点:它是一 棵空树它的左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL算法、替罪羊树、Treap、伸展树等。

如上图左边就是一颗平衡二叉树(AVL树),需要注意的是:平衡二叉树必须是一颗二叉排序树(BST),在BST树的基础上满足左右两个子树的高度差的绝对值不超过 1

3、使用代码构建AVL树

AVL树的构建又有些复杂,构建方式与BST树类似,是在BST树的基础上进行改造使之满足AVL树的特性,因此改造方式我们使用左旋转(单旋转)/右旋转(单旋转)/双旋转

注意:一定是构建BST树时左右两个子树的高度差的绝对值大于1才使用左旋转或右旋转!具体使用那种方式旋转由左右子树的高度决定。如左子树的高度大于右子树时,需要使用右旋转来降低左子树的高度。左右旋转都属于单旋转,只需旋转一次,非平衡二叉树就能转换成平衡二叉树,但是某些情况下单旋转不能完成平衡二叉树的转换,需要进行双旋转才行。

3.1、左旋转法构建

我们还是给定一串数组[4, 3, 6, 5, 7, 8],要求创建出数组对应的平衡二叉树(AVL)树。此数组对应的BST树如下:

我们发现,左右两个子树的高度差的绝对值已经大于1了,已经不再是一颗AVL树了,并且右子树的高度明显要高于左子树的高度,为了降低右子树的高度,就需要使用左旋转的方式改造BST树。左旋转思路分析:

  1. 创建一个新的节点newNode(以4这个值创建),创建一个新的节点,值等于当前根节点的值
  2. 把新节点的左子树设置为当前节点左子树newNode.left = left
  3. 把新节点的右子树设置为当前借节点的右子树的左子树newNode.right = right.left
  4. 把当前节点的值换为右子节点的值value = right.value
  5. 把当前节点的右子树设置成右子树的右子树right = right.right
  6. 把当前节点的左子树设置为新节点left = newLeft

代码示例:

package com.laizhenghua.tree;/*** @description: 平衡二叉树的构建* @date: 2021/11/12 20:13*/
public class BalancedBinaryTree {public static void main(String[] args) {// 创建数组int[] array = new int[]{4, 3, 6, 5, 7, 8};// 创建AVL树AVLTree avlTree = new AVLTree();for (int i = 0; i < array.length; i++) {Node node = new Node(array[i]);avlTree.add(node);}// 中序遍历avlTree.infixOrder();// 在没有使用左旋转处理之前(它是一个BST树)System.out.println("树的高度 = " + avlTree.getRoot().getTreeHeight()); // 4System.out.println("左子树高度 = " + avlTree.getRoot().getLeftHeight()); // 1System.out.println("右子树高度 = " + avlTree.getRoot().getRightHeight()); // 3// 毫无疑问右子树的高度减去左子树的高度已经大于1了,所以要使用左旋转处理// 改造Node类的add()方法(后面if判断为追加内容)// 再次执行main方法。树的高度 = 3 左子树高度 = 2 右子树高度 = 2}
}
// 创建AVL树
class AVLTree {private Node root;public AVLTree() {}public Node getRoot() {return this.root;}// 添加节点public void add(Node node) {if (root == null) {root = node;} else {root.add(node);}}// 中序遍历public void infixOrder() {if (root == null) {System.out.println("This tree is null");}root.infixOrder();}
}
// 创建Node节点
class Node {int value;Node left;Node right;public Node(int value) {this.value = value;}// 添加节点public void add(Node node) {if (node == null) {return;}if (this.value < node.value) {if (this.right == null) {this.right = node;} else {this.right.add(node); // 递归向左子树添加}} else {if (this.left == null) {this.left = node;} else {this.left.add(node); // 递归向右子树添加}}// 当添加完一个节点需要判断:(右子树的高度 - 左子树的高度) > 1  如果满足则做 左旋转 处理if (this.getRightHeight() - this.getLeftHeight() > 1) {this.leftRotation();}}// 中序遍历public void infixOrder() {if (this.left != null) {this.left.infixOrder();}System.out.println(this);if (this.right != null) {this.right.infixOrder();}}// 返回以该节点为根节点树的高度public int getTreeHeight() {return Math.max(left == null ? 0 : left.getTreeHeight(), right == null ? 0 : right.getTreeHeight()) + 1;}// 计算左子树高度public int getLeftHeight() {if (left == null) {return 0;}return left.getTreeHeight();}// 计算右子树高度public int getRightHeight() {if (right == null) {return 0;}return right.getTreeHeight();}// 左旋转方法private void leftRotation() {// 1、创建新的节点(值为根节点的值)Node newNode = new Node(this.value);// 2、把新节点的左子树设置为当前节点的左子树newNode.left = this.left;// 3、把新节点的右子树设置为带你过去节点的右子树的左子树newNode.right = right.left;// 4、把当前节点的值替换成右子节点的值this.value = this.right.value;// 5、把当前节点的右子树设置为右子树的右子树this.right = this.right.right;// 6、把当前节点的左子树(左子节点)设置为新的节点this.left = newNode;}@Overridepublic String toString() {return "Node{" + "value=" + value + "}";}
}

3.2、右旋转法构建

前面数组我们已经构建出来了对应的AVL树,右旋转法我们换一组数组,这组数组对应的BST树满足左子树的高度高于右子树的高度

{10, 12, 8, 9, 7, 6}为例。右旋转法也是和左旋转一样为了满足AVL树的特性、添加节点时(满足getLeftHeight() - getRightHeight() > 1 成立)做一次特殊处理(降低左子树的高度)。旋转过程如下:

  1. 创建一个新节点newNode(以10这个值创建),并且新节点的值等于当前根节点的值
  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 = newLeft

图示:

代码实现(基于左旋转代码新增方法):

// 右旋转方法
private void rightRotation() {Node newNode = new Node(this.value);newNode.right = this.right;newNode.left = this.left.right;this.value = this.left.value;this.left = this.left.left;this.right = newNode;
}

未做右旋转处理之前,中序遍历即树的高度

Node类的add()方法新增右旋转处理逻辑:

// 添加节点
public void add(Node node) {if (node == null) {return;}if (this.value < node.value) {if (this.right == null) {this.right = node;} else {this.right.add(node); // 递归向左子树添加}} else {if (this.left == null) {this.left = node;} else {this.left.add(node); // 递归向右子树添加}}// 当添加完一个节点需要判断:(右子树的高度 - 左子树的高度) > 1  如果满足则做 左旋转 处理if (this.getRightHeight() - this.getLeftHeight() > 1) {this.leftRotation();}// 当添加完一个节点需要判断:(左子树的高度 - 右子树的高度) > 1  如果满足则做 右旋转 处理if (this.getLeftHeight() - this.getRightHeight() > 1) {this.rightRotation();}
}

再次执行main方法:

3.3、双旋转法构建

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

比如数组:int[] array = new int[]{10, 11, 7, 6, 3, 9}; 或者是int[] array = new int[]{2, 1, 6, 5, 7, 3};

可以运行原来的代码验证!并没有转成AVL树。

解决思路分析:

  1. 满足右旋转的条件时,如果它的左子树的右子树高度大于它的左子树的高度,先对当前这个节点的左节点进行左旋转!再对当前节点进行右旋转。
  2. 满足左旋转的条件时,如果它的右子树的左子树高度大于它的右子树的高度,先对当前这个节点的右节点进行右旋转!再对当前节点进行左旋转。

如:

// 添加节点
public void add(Node node) {if (node == null) {return;}if (this.value < node.value) {if (this.right == null) {this.right = node;} else {this.right.add(node); // 递归向左子树添加}} else {if (this.left == null) {this.left = node;} else {this.left.add(node); // 递归向右子树添加}}// 当添加完一个节点需要判断:(右子树的高度 - 左子树的高度) > 1  如果满足则做 左旋转 处理if (this.getRightHeight() - this.getLeftHeight() > 1) {// 如果它的右子树的左子树高度大于它的右子树的高度if (this.right != null && this.right.getLeftHeight() > this.right.getRightHeight()) {// 先对右子节点进行右旋转this.right.rightRotation();this.leftRotation();} else {this.leftRotation();}return;}// 当添加完一个节点需要判断:(左子树的高度 - 右子树的高度) > 1  如果满足则做 右旋转 处理if (this.getLeftHeight() - this.getRightHeight() > 1) {// 如果它的左子树的右子树高度大于它的左子树的高度if (this.left != null && this.left.getRightHeight() > this.left.getLeftHeight()) {// 先对当前节点的左节点(左子树)进行左旋转this.left.leftRotation();this.rightRotation();} else {// 直接进行右旋转即可this.rightRotation();}}
}

mian方法验证

public static void main(String[] args) {int[] array = new int[]{10, 11, 7, 6, 3, 9};// 创建AVL树AVLTree avlTree = new AVLTree();for (int i = 0; i < array.length; i++) {Node node = new Node(array[i]);avlTree.add(node);}// 中序遍历avlTree.infixOrder();// 在没有使用左旋转处理之前(它是一个BST树)树的高度System.out.println("树的高度 = " + avlTree.getRoot().getTreeHeight());System.out.println("左子树高度 = " + avlTree.getRoot().getLeftHeight());System.out.println("右子树高度 = " + avlTree.getRoot().getRightHeight());}

输出结果:

END

THANK YOU

平衡二叉树(Balanced Binary Tree)相关推荐

  1. LeetCode 110. 平衡二叉树(Balanced Binary Tree) 15

    110. 平衡二叉树 110. Balanced Binary Tree 题目描述 给定一个二叉树,判断它是否是高度平衡的二叉树. 本题中,一棵高度平衡二叉树定义为: 一个二叉树每个节点的左右两个子树 ...

  2. LeetCode 110 Balanced Binary Tree 平衡二叉树

    LeetCode 110 Balanced Binary Tree Given a binary tree, determine if it is height-balanced. For this ...

  3. leetCode 110. Balanced Binary Tree 平衡二叉树

    110. Balanced Binary Tree Given a binary tree, determine if it is height-balanced. For this problem, ...

  4. [CareerCup] 4.1 Balanced Binary Tree 平衡二叉树

    4.1 Implement a function to check if a binary tree is balanced. For the purposes of this question, a ...

  5. leetcode - Balanced Binary Tree

    题目:Balanced Binary Tree Given a binary tree, determine if it is height-balanced. For this problem, a ...

  6. LeetCode 110. Balanced Binary Tree

    LeetCode 110. Balanced Binary Tree 本博客参考自:http://www.cnblogs.com/grandyang/p/4045660.html Solution1: ...

  7. LeetCode 110 Balanced Binary Tree

    LeetCode 110 Balanced Binary Tree Problem Description: 判断二叉树是不是平衡二叉树.所谓平衡二叉树,即每个节点的两个子树深度差的绝对值不超过1. ...

  8. 【leetcode】Balanced Binary Tree(middle)

    Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary ...

  9. LeetCode 110 Balanced Binary Tree(平衡二叉树)(*)

    版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csdn.net/NoMasp/article/details/5055 ...

最新文章

  1. Linux系统没有home分区,我的linux系统home分区挂不上了
  2. kafka的消费隔离级别(持续更新中)
  3. python一维数组合并_Python编程:如何将多个一维数组的元素交叉拼接成新的一维数组...
  4. T-SQL with关键字
  5. java es api jar包_Elasticsearch 搜索服务器 Java API 使用详解
  6. 计算机硬件无法启动不能读取文件,修复Windows出现的“文件或目录已损坏且无法读取”问题...
  7. 丢弃法(基于MXNet)
  8. Android 游戏开发入门 视频+源码
  9. 数据库系统及应用——班级管理系统
  10. Win10 Windows Defender自动删除破解工具的exe文件
  11. Java写的答题助手项目分析与总结
  12. ie10服务器运行失败,win7系统下无法安装ie10浏览器如何解决?win7系统下无法安装ie10浏览器三种解决方法...
  13. html radio vue,Vue.js选中动态绑定的radio的指定项_心病_前端开发者
  14. 流利阅读 2019.1.21 Top S. Korean animal rights group slammed for destroying dogs
  15. Redis+Tomcat实现集群的Session管理
  16. Swift 语言概览
  17. 大明战神戚继光带给程序员的启示
  18. android短信和彩信探秘threads
  19. 论文分享 Simple Baselines for Human Pose Estimation and Tracking
  20. 小区DMA漏控平台(Axure高保真原型)

热门文章

  1. 支付宝APP参数SDK转换URL网页链接
  2. 如何删除Mac下载PS后莫名其妙多出来的几个程序
  3. 记录一次zabbix网页端报错Assuming that agent dropped connection because of access permissions
  4. python删除excel指定行_python实现Excel删除特定行、拷贝指定行操作
  5. 玩客云手动设置IP地址
  6. “创业吃过饼,国企养过老,android开发零基础
  7. CLH Lock 原理
  8. Playing Atari with Deep Reinforcement Learning 学习笔记
  9. 穷举、贪心算法--泊松分酒(2)
  10. android 4g ram够么,4G还不够,安卓手机内存极限是多少