当前位置: 首页 > news >正文

AVL树(平衡二叉树)详细介绍与Java实现

文章目录

    • AVL树(平衡二叉搜索树)
      • 介绍
      • Java实现
        • 获取节点信息
        • ⭐左旋leftRotate
        • ⭐右旋rightRotate
        • ⭐插入节点 insert
          • 1.1.1. 左左情况(LL - Left Left)
          • 1.1.2. 右右情况(RR - Right Right)
          • 1.1.3. 左右情况(LR - Left Right)
          • 1.1.4. 右左情况(RL - Right Left)
          • 1.1.5. 完整代码演示
        • 删除节点 delete
          • 1.1.1. 删除叶子节点
          • 1.1.2. 删除有一个子节点的节点
          • 1.1.3. 删除有两个子节点的节点
        • 查找节点 search
        • 中序/前序遍历`inorder()/preorder()`
        • 检查树是否平衡`isBalanced()`
        • 获取树高度`getHeight()`

AVL树(平衡二叉搜索树)

介绍

定义:

AVL树是一种自平衡二叉搜索树,其中每个节点的左右子树的高度差(平衡因子)最多为1。当插入或删除节点导致平衡因子绝对值大于1时,需要通过旋转来恢复平衡。

平衡二叉树是一种特殊的二叉搜索树,它通过自动调整树的结构来保持较低的高度,从而确保各种操作的高效性。

主要目标是避免BST退化为链表,保证操作时间复杂度为O(log n)。

img

性质:

  • 平衡因子:节点左子树高度 - 右子树高度,绝对值 ≤ 1
  • 自平衡:插入/删除后自动调整结构恢复平衡
  • 高度平衡:树的高度始终保持在O(log n)

Java实现

  1. AVL树节点类

属性 :val(节点值)、left(左子节点)、right(右子节点)、height(节点高度)

@Data
public class AVLNode {/** 节点值 */int val;/** 左子节点 */AVLNode left;/** 右子节点 */AVLNode right;/** 节点高度 */int height;/*** 构造函数* @param val 节点值*/public AVLNode(int val) {this.val = val;this.left = null;this.right = null;this.height = 1; // 新节点的高度为1}}
  1. AVL树主类,实现核心功能主要方法 :
  • insert(int val) - 插入节点并自动平衡
  • delete(int val) - 删除节点并自动平衡
  • search(int val) - 查找节点
  • inorder()/preorder() - 中序/前序遍历
  • isBalanced() - 检查树是否平衡
  • getHeight() - 获取树高度
public class AVLTree {/** 根节点 */private AVLNode root;/*** 构造函数,初始化空的AVL树*/public AVLTree() {this.root = null;}// ......
}
获取节点信息
/*** 获取节点的高度* 如果节点为空,返回0* @param node 要获取高度的节点* @return 节点的高度*/
private int height(AVLNode node) {if (node == null) {return 0;}return node.getHeight();
}/*** 获取节点的平衡因子* 平衡因子 = 左子树高度 - 右子树高度* @param node 要计算平衡因子的节点* @return 平衡因子*/
private int getBalance(AVLNode node) {if (node == null) {return 0;}return height(node.getLeft()) - height(node.getRight());
}/*** 更新节点的高度* 节点高度 = 1 + 左右子树高度的最大值* @param node 要更新高度的节点*/
private void updateHeight(AVLNode node) {if (node != null) {int leftHeight = height(node.getLeft());int rightHeight = height(node.getRight());node.setHeight(Math.max(leftHeight, rightHeight) + 1);}
}
⭐左旋leftRotate

左旋的作用:相当于通过向上迁移树高差大于1的右子节点来降低树高的操作。

如图所示:将y节点上迁移,将y节点的左指针指向x,再将x的右指针指向T2

img

/*** 左旋操作(右右情况)* 用于处理右子树过高的不平衡情况* @param x 不平衡的节点* @return 旋转后的新根节点*/
private AVLNode leftRotate(AVLNode x) {AVLNode y = x.getRight();AVLNode T2 = y.getLeft();// 执行旋转y.setLeft(x);x.setRight(T2);// 更新高度updateHeight(x);updateHeight(y);return y;
}
⭐右旋rightRotate

右旋的作用:相当于通过向上迁移树高差大于1的左子节点来降低树高的操作。

如图所示:将x节点上迁移,将x节点的右指针指向y,再将y的左指针指向T2

img

/*** 右旋操作(左左情况)* 用于处理左子树过高的不平衡情况* @param y 不平衡的节点* @return 旋转后的新根节点*/
private AVLNode rightRotate(AVLNode y) {AVLNode x = y.getLeft();AVLNode T2 = x.getRight();// 执行旋转x.setRight(y);y.setLeft(T2);// 更新高度updateHeight(y);updateHeight(x);return x;
}
⭐插入节点 insert

平衡因子 = 左子树高度 - 右子树高度

  • 平衡因子 > 1:左子树过高(需要右旋)
  • 平衡因子 < -1:右子树过高(需要左旋)

插入节点有4种情况:

1.1.1. 左左情况(LL - Left Left)
  1. 触发条件 : balance > 1 && val < node.getLeft().getVal()
  2. 新节点插入到左子树的左子树中
  3. 需要执行右旋操作
// 左左情况(左子树的左子树过高)
if (balance > 1 && val < node.getLeft().getVal()) {return rightRotate(node);
}
1.1.2. 右右情况(RR - Right Right)
  1. 触发条件 : balance < -1 && val > node.getRight().getVal()
  2. 新节点插入到右子树的右子树中
  3. 需要执行 左旋操作
// 右右情况(右子树的右子树过高)
if (balance < -1 && val > node.getRight().getVal()) {return leftRotate(node);
}

img

1.1.3. 左右情况(LR - Left Right)
  1. 触发条件 :balance > 1 && val > node.getLeft().getVal()
  2. 新节点插入到左子树的右子树中
  3. 需要先 左旋 再 右旋 (两步操作)
// 左右情况(左子树的右子树过高)
if (balance > 1 && val > node.getLeft().getVal()) {node.setLeft(leftRotate(node.getLeft()));  // 先对左子树左旋return rightRotate(node);                  // 再对当前节点右旋
}

img

1.1.4. 右左情况(RL - Right Left)
  1. 触发条件 :balance < -1 && val < node.getRight().getVal()
  2. 新节点插入到右子树的左子树中
  3. 需要先 右旋 再 左旋 (两步操作)
// 右左情况(右子树的左子树过高)
if (balance < -1 && val < node.getRight().getVal()) {node.setRight(rightRotate(node.getRight())); // 先对右子树右旋return leftRotate(node);                     // 再对当前节点左旋
}

img

1.1.5. 完整代码演示
/*** 插入新节点到AVL树* @param val 要插入的值*/
public void insert(int val) {root = insert(root, val);
}/*** 递归插入新节点并保持平衡* @param node 当前节点* @param val 要插入的值* @return 插入后的节点*/
private AVLNode insert(AVLNode node, int val) {// 1. 执行标准的BST插入if (node == null) {return new AVLNode(val);}if (val < node.getVal()) {node.setLeft(insert(node.getLeft(), val));} else if (val > node.getVal()) {node.setRight(insert(node.getRight(), val));} else {// 不允许重复值return node;}// 2. 更新当前节点的高度updateHeight(node);// 3. 获取平衡因子检查是否平衡int balance = getBalance(node);// 4. 如果不平衡,有四种情况需要处理// 左左情况(左子树的左子树过高)if (balance > 1 && val < node.getLeft().getVal()) {return rightRotate(node);}// 右右情况(右子树的右子树过高)if (balance < -1 && val > node.getRight().getVal()) {return leftRotate(node);}// 左右情况(左子树的右子树过高)if (balance > 1 && val > node.getLeft().getVal()) {node.setLeft(leftRotate(node.getLeft()));return rightRotate(node);}// 右左情况(右子树的左子树过高)if (balance < -1 && val < node.getRight().getVal()) {node.setRight(rightRotate(node.getRight()));return leftRotate(node);}// 如果平衡,直接返回节点return node;
}
删除节点 delete
1.1.1. 删除叶子节点

img

1.1.2. 删除有一个子节点的节点

img

1.1.3. 删除有两个子节点的节点

用中序后继(右子树的最小值)替换被删除节点,然后删除中序后继

img

/*** 从AVL树中删除指定值的节点* @param val 要删除的值*/
public void delete(int val) {root = delete(root, val);
}/*** 递归删除节点并保持平衡* @param node 当前节点* @param val 要删除的值* @return 删除后的节点*/
private AVLNode delete(AVLNode node, int val) {// 1. 执行标准的BST删除if (node == null) {return null;}if (val < node.getVal()) {node.setLeft(delete(node.getLeft(), val));} else if (val > node.getVal()) {node.setRight(delete(node.getRight(), val));} else {// 找到要删除的节点// 节点只有一个子节点或没有子节点if (node.getLeft() == null || node.getRight() == null) {AVLNode temp = null;if (temp == node.getLeft()) {temp = node.getRight();} else {temp = node.getLeft();}// 没有子节点的情况if (temp == null) {temp = node;node = null;} else {// 有一个子节点的情况node = temp;}} else {// 节点有两个子节点:获取中序后继(右子树的最小值)AVLNode temp = minValueNode(node.getRight());node.setVal(temp.getVal());node.setRight(delete(node.getRight(), temp.getVal()));}}// 如果树只有一个节点,直接返回if (node == null) {return null;}// 2. 更新当前节点的高度updateHeight(node);// 3. 获取平衡因子检查是否平衡int balance = getBalance(node);// 4. 如果不平衡,有四种情况需要处理// 左左情况if (balance > 1 && getBalance(node.getLeft()) >= 0) {return rightRotate(node);}// 左右情况if (balance > 1 && getBalance(node.getLeft()) < 0) {node.setLeft(leftRotate(node.getLeft()));return rightRotate(node);}// 右右情况if (balance < -1 && getBalance(node.getRight()) <= 0) {return leftRotate(node);}// 右左情况if (balance < -1 && getBalance(node.getRight()) > 0) {node.setRight(rightRotate(node.getRight()));return leftRotate(node);}return node;
}
查找节点 search
/*** 查找子树中的最小值节点* @param node 子树根节点* @return 最小值节点*/
private AVLNode minValueNode(AVLNode node) {AVLNode current = node;while (current.getLeft() != null) {current = current.getLeft();}return current;
}/*** 在AVL树中查找指定值* @param val 要查找的值* @return 如果找到返回true,否则返回false*/
public boolean search(int val) {return search(root, val);
}/*** 递归查找指定值* @param node 当前节点* @param val 要查找的值* @return 如果找到返回true,否则返回false*/
private boolean search(AVLNode node, int val) {if (node == null) {return false;}if (val == node.getVal()) {return true;} else if (val < node.getVal()) {return search(node.getLeft(), val);} else {return search(node.getRight(), val);}
}
中序/前序遍历inorder()/preorder()
/*** 中序遍历AVL树* 对于AVL树,中序遍历会产生有序序列*/
public void inorder() {inorder(root);System.out.println();
}/*** 递归执行中序遍历* @param node 当前节点*/
private void inorder(AVLNode node) {if (node != null) {inorder(node.getLeft());System.out.print(node.getVal() + "(" + node.getHeight() + ") ");inorder(node.getRight());}
}/*** 前序遍历AVL树*/
public void preorder() {preorder(root);System.out.println();
}/*** 递归执行前序遍历* @param node 当前节点*/
private void preorder(AVLNode node) {if (node != null) {System.out.print(node.getVal() + "(" + node.getHeight() + ") ");preorder(node.getLeft());preorder(node.getRight());}
}
检查树是否平衡isBalanced()
/*** 递归检查子树是否平衡* @param node 当前节点* @return 如果平衡返回true,否则返回false*/
private boolean isBalanced(AVLNode node) {if (node == null) {return true;}int balance = getBalance(node);if (Math.abs(balance) > 1) {return false;}return isBalanced(node.getLeft()) && isBalanced(node.getRight());
}
获取树高度getHeight()
/*** 获取树的高度* @return 树的高度*/
public int getHeight() {return height(root);
}/*** 获取根节点* @return 根节点*/
public AVLNode getRoot() {return root;
}/*** 主方法,用于测试AVL树的功能* @param args 命令行参数*/
http://www.dtcms.com/a/499151.html

相关文章:

  • 2025年市场岗位能力重构与跨领域转型路径分析
  • SQL UNIQUE约束详解
  • 【unity实战】MapMagic 2实战例子
  • 系统找不到文件
  • 网站建设综合实训总结有谁会设制网站
  • 什么是Redis的缓存问题,以及如何解决
  • Python遗传算法详解:从理论到实践
  • 技术支持 东莞网站建设 轴承信宜网站建设公司
  • CSS基础知识(3)
  • git分支管理介绍和stash命令
  • 建个网站视频教程团队拓展训练感悟
  • 做网站应选那个主题龙岩微信小程序定制
  • Linux学习笔记--GPIO子系统和PinCtrl子系统
  • SpringBoot外部配置打包
  • 通达信--超级盘口
  • 基于单片机的开尔文电路电阻测量WIFI上传设计
  • 矽塔 SA8210 输入耐压36V 6A过流保护阈值 过压/过流保护芯片 SOT23-6/DFN2X2-8
  • 永年做网站收集链接 做网站
  • Linux----权限
  • 深入理解 PHP 框架里的设计模式
  • 西安网站工作室做外贸网站哪家的好
  • 如何用python写一个有字数上限的文字区块链?
  • 算能 CV184 智能相机整体方案介绍
  • 广州皮具网站建设湖南手机版建站系统信息
  • 大型网站建设哪里济南兴田德润实惠吗临沂市网站建设公司
  • Linux系统:线程介绍与POSIX线程库实现线程控制
  • ITP新增安全测试模块:构建自动化安全防护体系
  • 【C++/Lua联合开发】 (二) Lua调用C++函数
  • 基于Simulink的混动汽车模型建模与仿真,包含发动机管理,电机,电池管理以及混动汽车物理模型等
  • 网站备案都需要什么网站群项目建设实施进度计划