这目录

  • 9.10 平衡(AVL)二叉树
    • 完整代码
  • 本次平衡二叉树教程出自韩顺平的数据结构与算法

9.10 平衡(AVL)二叉树

引入

基本介绍

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

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

左旋转分析:数列{4,3,6,5,7,8}二叉排序树43   65    78
问题:当插入8时
rightHeight()-leftHeight() > 1,不再是一颗AVL树
可以进行左旋转处理使其变成AVL树
1)创建一个新的节点newNode(以根节点的值创建),创建一个新的节点
值等于当前根节点的值
2)//把新节点的左子树设置为当前节点的左子树
newNode.left = left;
3)//把新节点的右子树设置为当前节点的右子树的左子树
newNode.right = right.left;
4)//把当前节点的值转换成右子节点的值
value = right.value;
5)//把当前节点的右子树设置成右子树的右子树
right=right.right;
6)//把当前节点的左子树设置成新节点
left = newNode;左旋后的二叉排序树变成了AVL树64     73     5    8右旋转分析:
数列{10,12,8,9,7,6}二叉排序树108    127    96问题:当插入6时
leftHeight()-rightHeight() > 1,不再是一颗AVL树
可以进行右旋转处理使其变成AVL树
1)创建一个新的节点newNode(以根节点的值创建),创建一个新的节点
值等于当前根节点的值
2)//把新节点的左子树设置为当前节点的左子树的右子树
newNode.left = left.right;
3)//把新节点的右子树设置为当前节点的右子树
newNode.right = right;
4)//把当前节点的值转换成左子节点的值
value = left.value;
5)//把当前节点的左子树设置成左子树左子树
left = left.left;
6)//把当前节点的右子树设置成新节点
right = newNode;      右旋后的二叉排序树变成AVL树87     106     9    12

左旋二叉搜索树、右旋二叉搜索树

什么时候开始左旋? 什么时候开始右旋?当然是根节点的左右子树的高度差大于1

rightHeight()-leftHeight() > 1的时候左旋

leftHeight()-rightHeight() > 1的时候右旋

所以,无论左旋还是右旋,还得要求出树的左右子树的高度

代码实现

 /*** 返回左子树的高度*/public int getLeftHeight(){if (left == null){return 0;}return left.getHeight();}/*** 返回右子树的高度*/public int getRightHeight(){if (right == null){return 0;}return  right.getHeight();}/*** 返回树的高度*/public int getHeight(){return Math.max(left == null ? 0 : left.getHeight(), right == null ? 0 : right.getHeight()) + 1;}

左旋代码

    /*** 左旋转*/public void leftRotate(){//创建新节点,以根节点的值Node newNode = new Node(value);//把新节点的左子树设置为当前节点的左子树newNode.left = left;//把新节点的右子树设置为当前节点的右子树的左子树newNode.right = right.left;//把当前节点的值转换成右子节点的值value = right.value;//把当前节点的右子树设置成右子树的右子树right=right.right;//把当前节点的左子树设置成新节点left = newNode;}

右旋代码

/*** 右旋转*/public void rightRotate() {//创建新节点,以根节点的值Node newNode = new Node(value);//创建一个新的节点newNode(以根节点的值创建),创建一个新的节点//值等于当前根节点的值//把新节点的左子树设置为当前节点的左子树的右子树newNode.left = left.right;//把新节点的右子树设置为当前节点的右子树newNode.right = right;//把当前节点的值转换成左子节点的值value = left.value;//把当前节点的左子树设置成左子树左子树left = left.left;//把当前节点的右子树设置成新节点right = newNode;}

AVL实现双旋

有时候,符合左旋或者符合右旋的二叉搜索树的结构,旋转之后不一定能实现AVL树。所以需要左旋配合右旋使用 或者右旋配合左旋使用。

如下图例子

这是因为 7 这个节点为根节点的树的右子节点高度大于它的左子树的高度

        76  89
问题分析:
1、当符合右旋的条件时
2、如果它的左子树的右子节点高度大于它的左子树的高度
3、先对当前节点的左节点(7为根节点)进行左旋87   96
4、再对当前节点(10)进行右旋的操作即可双旋得到的AVL树87   106    9   11

添加二叉树节点的时候调用左右旋

 /*** 添加节点的方法*** @param node 传入的节点*/public void add(Node node){if ( node ==null){ //传入的节点为null,直接返回return;}if ( node.value < this.value){ //传入的节点小于当前节点,就放入当前节点的左子树if (this.left == null){ //当前节点的left为null,直接放入this.left = node;}else { //当前节点的left不为null,递归遍历当前节点的左子树,直到找到某个节点的left为null,插入传入的节点this.left.add(node);}}else { //传入的节点大于于当前节点,就放入当前节点的右子树if (this.right == null){//当前节点的right为null,直接放入this.right = node;}else {//当前节点的right不为null,递归遍历当前节点的左子树,直到找到某个节点的right为null,插入传入的节点this.right.add(node);}}//rightHeight()-leftHeight() > 1,不再是一颗AVL树//可以进行旋转处理使其变成AVL树if ( getRightHeight() - getLeftHeight() > 1 ){//如果它的右子树 的左子树的高度大于左子树if (right != null && right.getLeftHeight() > right.getRightHeight()){//先对当前节点的右节点(右子树)-》右旋right.rightRotate();//再对当前节点进行右旋leftRotate();}else {leftRotate();}return; //这个步骤必须要}//leftHeight()-rightHeight() > 1,不再是一颗AVL树//可以进行旋转处理使其变成AVL树if (  getLeftHeight()-getRightHeight()  > 1 ){//如果它的左子树  的右子树的高度左子树if (left != null && left.getRightHeight() > left.getLeftHeight()){//先对当前节点的左节点(左子树)-》左旋left.leftRotate();//再对当前节点进行右旋rightRotate();}else {rightRotate();}}}

完整代码

package com.ldm.AVL;/*** @author 梁东明* 2022/9/7* 人生建议:看不懂的方法或者类记得CTRL + 点击 看看源码或者注解* 点击setting在Editor 的File and Code Templates 修改*/
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};AVLTree avlTree = new AVLTree();for (int i = 0; i < arr.length; i++) {avlTree.add(new Node(arr[i]));}System.out.println("中序遍历~");avlTree.infixOrder();System.out.println("树的高度是:" + avlTree.getRoot().getHeight());System.out.println("左子树的高度是:" + avlTree.getRoot().getLeftHeight());System.out.println("右子树的高度是:" + avlTree.getRoot().getRightHeight());System.out.println("当前根节点是:" + avlTree.getRoot());}
}
class AVLTree{private Node root;public Node getRoot() {return root;}/*** 搜索要删除的节点*/public Node search(int value){if ( root == null){return null;}else {return root.search(value);}}/*** 搜索父节点*/public Node searchParent(int value){if ( root == null){return null;}else {return root.searchParent(value);}}/***1、返回以node为根节点的二叉排序树的最小节点的值*2、删除node为根节点的二叉排序树的最小节点** @param node 传入的节点(当作当前二叉树的根节点)* @return 返回以node为根节点的二叉排序树的最小节点的值*/public int delRightTreeMin(Node node){Node target = node;//循环查找左子节点,直到找到最小值while (target.left != null){target = target.left;}//退出while循环后,最小值就找到了//把它删除了delNode(target.value);return target.value;}/*** 删除节点** @param value 要删除节点的值*/public void delNode(int value){if ( root == null){return;}else {//1、需要找到要删除的节点 targetNodeNode targetNode = search(value);//如果没有找到就直接返回;if (targetNode == null){return;}//如果二叉排序树只有根节点,把根节点置null;if ( root.left == null && root.right ==null){root = null;return;}//找到要删除的节点的父节点Node parent = searchParent(value);//第一种情况:如果要删除的是叶子节点if (targetNode.left == null && targetNode.right == null){//判断要删除的节点是其父节点左节点还是右节点if (parent.left != null && parent.left.value == value ){parent.left = null;}else if (parent.right != null && parent.right.value == value){parent.right = null;}} //第三种情况:如果要删除的节点有左右子树else if (targetNode.left != null && targetNode.right != null){int minValue = delRightTreeMin(targetNode.right);targetNode.value = minValue;}//第二种情况:如果要删除的节点有一个子树// 因为第二种情况的条件最复杂,所以用排除法先把第一第三种情况的条件判断,// 剩下的就是第二种情况的条件,直接不用if语句判断。无用的小知识又增加了!//你也可以把第二种情况的条件语句写出来,反正挺长的,你喜欢好了!//if( (targetNode.left != null && targetNode.right ==null) ||// (targetNode.right != null && targetNode.left == null) )else {//如果要删除的节点只有左子树if (targetNode.left != null){if ( parent != null){//如果targetNode是parent的左子节点if (parent.left.value == value){parent.left = targetNode.left;} //如果targetNode是parent的右子节点else if (parent.right.value == value){parent.right = targetNode.left;}}else {root = targetNode.left;}}//如果要删除的节点只有右子树else {if (parent != null){//如果targetNode是parent的左子节点if (parent.left.value == value){parent.left = targetNode.right;} //如果targetNode是parent的右子节点else if (parent.right.value == value){parent.right = targetNode.right;}}else {root = targetNode.right;}}}}}/*** 中缀遍历*/public void infixOrder(){if (root !=null){root.infixOrder();}else {System.out.println("这是一个空树");}}/*** 添加节点的方法** @param node 节点*/public void add(Node node){if ( root == null){root = node;}else {root.add(node);}}}class Node {int value;Node left;Node right;public Node(int value) {this.value = value;}/*** 返回左子树的高度*/public int getLeftHeight(){if (left == null){return 0;}return left.getHeight();}/*** 返回右子树的高度*/public int getRightHeight(){if (right == null){return 0;}return  right.getHeight();}/*** 返回树的高度*/public int getHeight(){return Math.max(left == null ? 0 : left.getHeight(), right == null ? 0 : right.getHeight()) + 1;}/*** 左旋转*/public void leftRotate(){//创建新节点,以根节点的值Node newNode = new Node(value);//把新节点的左子树设置为当前节点的左子树newNode.left = left;//把新节点的右子树设置为当前节点的右子树的左子树newNode.right = right.left;//把当前节点的值转换成右子节点的值value = right.value;//把当前节点的右子树设置成右子树的右子树right=right.right;//把当前节点的左子树设置成新节点left = newNode;}/*** 右旋转*/public void rightRotate() {//创建新节点,以根节点的值Node newNode = new Node(value);//创建一个新的节点newNode(以根节点的值创建),创建一个新的节点//值等于当前根节点的值//把新节点的左子树设置为当前节点的左子树的右子树newNode.left = left.right;//把新节点的右子树设置为当前节点的右子树newNode.right = right;//把当前节点的值转换成左子节点的值value = left.value;//把当前节点的左子树设置成左子树左子树left = left.left;//把当前节点的右子树设置成新节点right = newNode;}@Overridepublic String toString() {return "Node{" +"value=" + value +'}';}/*** 添加节点的方法*** @param node 传入的节点*/public void add(Node node){if ( node ==null){ //传入的节点为null,直接返回return;}if ( node.value < this.value){ //传入的节点小于当前节点,就放入当前节点的左子树if (this.left == null){ //当前节点的left为null,直接放入this.left = node;}else { //当前节点的left不为null,递归遍历当前节点的左子树,直到找到某个节点的left为null,插入传入的节点this.left.add(node);}}else { //传入的节点大于于当前节点,就放入当前节点的右子树if (this.right == null){//当前节点的right为null,直接放入this.right = node;}else {//当前节点的right不为null,递归遍历当前节点的左子树,直到找到某个节点的right为null,插入传入的节点this.right.add(node);}}//rightHeight()-leftHeight() > 1,不再是一颗AVL树//可以进行旋转处理使其变成AVL树if ( getRightHeight() - getLeftHeight() > 1 ){//如果它的右子树 的左子树的高度大于左子树if (right != null && right.getLeftHeight() > right.getRightHeight()){//先对当前节点的右节点(右子树)-》右旋right.rightRotate();//再对当前节点进行右旋leftRotate();}else {leftRotate();}return; //这个步骤必须要}//leftHeight()-rightHeight() > 1,不再是一颗AVL树//可以进行旋转处理使其变成AVL树if (  getLeftHeight()-getRightHeight()  > 1 ){//如果它的左子树  的右子树的高度左子树if (left != null && left.getRightHeight() > left.getLeftHeight()){//先对当前节点的左节点(左子树)-》左旋left.leftRotate();//再对当前节点进行右旋rightRotate();}else {rightRotate();}}}/*** 中缀遍历*/public void infixOrder(){if (this.left != null){this.left.infixOrder();}System.out.println(this);if (this.right != null){this.right.infixOrder();}}/*** 搜索要删除的节点** @param value 希望删除的节点的值* @return 找到就返回该节点,否则就返回null*/public Node search(int value){//找到该节点就返回if (value == this.value){return this;}else if ( value < this.value ){if (this.left == null){ //树中没有该值的节点,就返回nullreturn null;}return this.left.search(value);}else {if (this.right == null){//树中没有该值的节点,就返回nullreturn null;}return this.right.search(value);}}/*** 搜索父节点** @param value 希望删除的节点的值* @return 返回的是要删除的节点的父节点。如果没有就返回null*/public Node searchParent(int value){//如果当前节点就是要删除的节点的父节点,返回if ( (this.left != null && this.left .value == value) ||(this.right != null && this.right .value == value)){return this;}else {//如果要查找的值小于当前节点的值,且当前节点的左子树不为null,就递归在左子树查找if (value < this.value && this.left != null){return this.left.searchParent(value);}else if (value >= this.value && this.right != null){//如果要查找的值大于或等于当前节点的值,且当前节点的右子树不为null,就递归在右子树查找return this.right.searchParent(value);}else {return null;  //没有找到父节点}}}}

本次平衡二叉树教程出自韩顺平的数据结构与算法

数据结构和算法教程,哔哩哔哩详细教程
在 135-141p.

最后,认识一下,我是小白。努力成为一名合格的程序员。期待与你的下次相遇。

平衡二叉树(Java)相关推荐

  1. 平衡二叉树(Java) -- 左旋旋右旋旋

    平衡二叉树 平衡二叉树(Java) -- 左旋旋右旋旋 平衡二叉树 -- 代码 平衡二叉树(Java) – 左旋旋右旋旋   平衡二叉树全称平衡二叉搜索树,所以首先具备了二叉搜索树的特性,因为二叉搜索 ...

  2. leetcode-110:判断平衡二叉树 Java

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

  3. 2021年备战面试--算法13

    public static void main(String[] args) {          System.out.println(getMax(query)); 0. 时间复杂度:JAVA数据 ...

  4. LeetCode855:考场就座

    如题: 思路分析: 我们每次挑选座位的时候,需要里最近的人距离最大,也就是说如果要坐在两个人之间,应该坐在中点的位置 因此我们可以将两个座位抽象成一个线段,第一个人是线段开始位置start,第二个人是 ...

  5. java TreeMap 源代码分析 平衡二叉树

    TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点. TreeSet 和 TreeMap 的关系 为了让大家了解 TreeMap 和 Tre ...

  6. java实现平衡二叉树(详细分析)

    package com.utils; import java.util.Iterator; import java.util.NoSuchElementException; /*** 平衡二叉树* 定 ...

  7. 高度平衡二叉树的构建_平衡二叉树建立及其增删改查(JAVA)

    平衡二叉树:指的是左右子树高度差的绝对值不超过一的二叉排序树. 主要思路:1.用左高度跟右高度代替平衡因子,大于1进行L~调整,小于-1进行R~调整 2.每次插入都通过递归计算一次各结点高度,然后进行 ...

  8. 映射表map(平衡二叉树实现)_手动实现Java集合容器之TreeMap(上)

    上一篇我们手写了HashMap,还有一个很重要的Map的实现类TreeMap.打开源码第一句话:* A Red-Black tree based {@link NavigableMap} implem ...

  9. avl二叉树 java_平衡二叉树之AVL树(Adelson-Velsky and Landis Tree)简介及Java实现

    平衡二叉树之AVL树(Adelson-Velsky and Landis Tree)简介及Java实现 标签:#二叉树##数据结构##自平衡二叉树# 时间:2018/10/27 09:30:01 作者 ...

  10. 使用Java实现平衡二叉树

    使用Java实现平衡二叉树 二叉树是一种较为复杂的数据结构,二叉树算法在数据查询时的时间复杂度为 O(log n)(n为保存元素个数). 但是普通的二叉树在数据添加和删除时很容易出现树结构不平衡问题 ...

最新文章

  1. python导入模块的三种方法,例子: import numpy和from numpy import * (import matplotlib 和 from matplotlib import *)
  2. SpringBoot - Spring Boot 应用剖析
  3. Sersync实现触发式文件同步
  4. 常用json框架介绍和Jackson返回结果处理
  5. SAP Spartacus lock focus Directive的工作原理示意图
  6. cmd pc如何开多个微信_抖音打击刷赞刷粉,240多个百万粉丝大V被封;微信PC版再更新...
  7. imp导入dmp文件报:IMP-00038: 无法转换为环境字符集句柄IMP-00000: 未成功终止导入
  8. 在CentOS上安装NodeJS_v14.x
  9. Python精通-Python列表操作
  10. zabbix-通过自动注册自动添加主机
  11. Vista忘记密码如何登录?
  12. 管理感悟:主管加班,员工才会加班
  13. 控制台 钢铁雄心2_钢铁雄心4控制台 (2)
  14. 编译原理研究性学习专题 2——递归下降语法分析设计原理与实现
  15. 武汉理工大学 计算机学院院长,熊盛武:武汉理工大学计算机科学与技术学院院长、教授...
  16. STM32定时 计算公式
  17. GSM-GPRS-WCDMA-LTE-5g 的总结
  18. 如何建设企业入侵防御体系
  19. 使用左氧氟沙星4注意
  20. python 菜品识别_菜品识别-python

热门文章

  1. CMake设置Visual Studio工程的调试环境变量和工作目录cwd的方法
  2. Flutter绘制指南09-动画曲线和方法
  3. 计算机学院寝室文明风景线活动,计算机工程学院院文明寝室评选活动圆满结束...
  4. 第二阶段--团队冲刺--第九天
  5. 360极速浏览器极速模式通过hosts文件切换兼容模式bat脚本
  6. 什么是数字孪生?【深度解析】
  7. 浏览器中的画中画(Picture-in-Picture)模式及其 API
  8. 背包问题_(DP经典),一,(01背包,填满背包)
  9. MySQL(学习笔记)
  10. [《关于外婆家的一些记忆》闲笔记事集]2012年1月22日