平衡二叉树(Balanced Binary Tree)
平衡二叉树(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树。左旋转思路分析:
- 创建一个新的节点
newNode
(以4这个值创建),创建一个新的节点,值等于当前根节点的值 - 把新节点的左子树设置为当前节点左子树
newNode.left = left
- 把新节点的右子树设置为当前借节点的右子树的左子树
newNode.right = right.left
- 把当前节点的值换为右子节点的值
value = right.value
- 把当前节点的右子树设置成右子树的右子树
right = right.right
- 把当前节点的左子树设置为新节点
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 成立
)做一次特殊处理(降低左子树的高度)。旋转过程如下:
- 创建一个新节点
newNode
(以10这个值创建),并且新节点的值等于当前根节点的值 - 把新节点的右子树设置为当前节点的右子树
newNode.right = this.right
- 把新节点的左子树设置为当前节点的左子树的右子树
newNode.left = this.left.right
- 把当前节点的值换为左子节点的值
this.value = this.left.value
- 把当前节点的左子树设置为左子树的左子树
this.left = this.left.left
- 把当前节点的右子树设置为新节点
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树。
解决思路分析:
- 满足右旋转的条件时,如果它的左子树的右子树高度大于它的左子树的高度,先对当前这个节点的左节点进行左旋转!再对当前节点进行右旋转。
- 满足左旋转的条件时,如果它的右子树的左子树高度大于它的右子树的高度,先对当前这个节点的右节点进行右旋转!再对当前节点进行左旋转。
如:
// 添加节点
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)相关推荐
- LeetCode 110. 平衡二叉树(Balanced Binary Tree) 15
110. 平衡二叉树 110. Balanced Binary Tree 题目描述 给定一个二叉树,判断它是否是高度平衡的二叉树. 本题中,一棵高度平衡二叉树定义为: 一个二叉树每个节点的左右两个子树 ...
- LeetCode 110 Balanced Binary Tree 平衡二叉树
LeetCode 110 Balanced Binary Tree Given a binary tree, determine if it is height-balanced. For this ...
- leetCode 110. Balanced Binary Tree 平衡二叉树
110. Balanced Binary Tree Given a binary tree, determine if it is height-balanced. For this problem, ...
- [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 ...
- leetcode - Balanced Binary Tree
题目:Balanced Binary Tree Given a binary tree, determine if it is height-balanced. For this problem, a ...
- LeetCode 110. Balanced Binary Tree
LeetCode 110. Balanced Binary Tree 本博客参考自:http://www.cnblogs.com/grandyang/p/4045660.html Solution1: ...
- LeetCode 110 Balanced Binary Tree
LeetCode 110 Balanced Binary Tree Problem Description: 判断二叉树是不是平衡二叉树.所谓平衡二叉树,即每个节点的两个子树深度差的绝对值不超过1. ...
- 【leetcode】Balanced Binary Tree(middle)
Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary ...
- LeetCode 110 Balanced Binary Tree(平衡二叉树)(*)
版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csdn.net/NoMasp/article/details/5055 ...
最新文章
- Linux系统没有home分区,我的linux系统home分区挂不上了
- kafka的消费隔离级别(持续更新中)
- python一维数组合并_Python编程:如何将多个一维数组的元素交叉拼接成新的一维数组...
- T-SQL with关键字
- java es api jar包_Elasticsearch 搜索服务器 Java API 使用详解
- 计算机硬件无法启动不能读取文件,修复Windows出现的“文件或目录已损坏且无法读取”问题...
- 丢弃法(基于MXNet)
- Android 游戏开发入门 视频+源码
- 数据库系统及应用——班级管理系统
- Win10 Windows Defender自动删除破解工具的exe文件
- Java写的答题助手项目分析与总结
- ie10服务器运行失败,win7系统下无法安装ie10浏览器如何解决?win7系统下无法安装ie10浏览器三种解决方法...
- html radio vue,Vue.js选中动态绑定的radio的指定项_心病_前端开发者
- 流利阅读 2019.1.21 Top S. Korean animal rights group slammed for destroying dogs
- Redis+Tomcat实现集群的Session管理
- Swift 语言概览
- 大明战神戚继光带给程序员的启示
- android短信和彩信探秘threads
- 论文分享 Simple Baselines for Human Pose Estimation and Tracking
- 小区DMA漏控平台(Axure高保真原型)
热门文章
- 支付宝APP参数SDK转换URL网页链接
- 如何删除Mac下载PS后莫名其妙多出来的几个程序
- 记录一次zabbix网页端报错Assuming that agent dropped connection because of access permissions
- python删除excel指定行_python实现Excel删除特定行、拷贝指定行操作
- 玩客云手动设置IP地址
- “创业吃过饼,国企养过老,android开发零基础
- CLH Lock 原理
- Playing Atari with Deep Reinforcement Learning 学习笔记
- 穷举、贪心算法--泊松分酒(2)
- android 4g ram够么,4G还不够,安卓手机内存极限是多少