目录

1、AVL树的概念

2、AVL树定义节点

3、AVL树的插入

4、AVL树的旋转

4.1、新节点插入较高左子树的左侧——右单旋

4.2、新节点插入较高右子树的右侧——左单旋

4.3、新节点插入较高左子树的右侧——左右双旋

4.4、新节点插入较高右子树的左侧——右左双旋

5、AVL树的验证

代码汇总:

6、AVL树的删除

7、AVL树性能分析


1、AVL树的概念

AVL树可能是一颗空树,或者是一颗具有性质的二叉搜索树:

  • 左右子树都是AVL树
  • 左右子树高度之差【即平衡因子】的绝对值不超过1

注:

  • 如果一棵二叉搜索树是高度平衡的,他就是AVL树
  • 如果他有n个节点,其高度可保持在O(logn),搜索时间复杂度:O(logn)
  • 当前节点的平衡因子 = 右子树高度 - 左子树高度

例:


2、AVL树定义节点


public class AVLTree {static class TreeNode {public int val;public int bf;//平衡因子public TreeNode left;//左孩子引用public TreeNode right;//右孩子引用public TreeNode parent;//父节点引用public TreeNode(int val) {this.val = val;}}public TreeNode root;//根节点
}

3、AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此,AVL树的插入,咱们呢,就可以分为两步:

  • 按照二叉搜索树的方式插入新的节点
  • 调整节点的平衡因子

代码:

     /*** 插入新的节点* @param val* @return 插入成功,返回true,失败返回false*/public boolean insert(int val) {//1、按照二叉搜索树的方式插入新的节点if(root == null) {root = new TreeNode(val);return true;}TreeNode node = new TreeNode(val);TreeNode parent = null;TreeNode cur = root;while(cur != null) {if(cur.val < val){parent = cur;cur = cur.right;} else if(cur.val == val) {return false;} else {parent = cur;cur = cur.left;}}if(parent.val < val) {parent.right = node;} else {parent.left = node;}node.parent = parent;cur = node;//2、调节平衡因子}

看一个例子:

调节平衡因子,需要注意的点:

cur插入后,parent的平衡因子一定需要调整,在插入之前,parent的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:

  • 如果cur插入到parent的左侧,只需给parent的平衡因子-1即可
  • 如果cur插入到parent的右侧,只需给parent的平衡因子+1即可

平衡因子更新时,存在一个问题,我们需要更新到什么时候停止呢?难道每次都要更新到根节点?

此时:parent的平衡因子可能有三种情况:0,正负1, 正负2

  • 如果parent的平衡因子为0,说明插入之前parent的平衡因子为正负1,插入后被调整成0,此时满足AVL树的性质,插入成功
  • 如果parent的平衡因子为正负1,说明插入前parent的平衡因子一定为0,插入后被更新成正负1,此时以parent为根的树的高度增加,需要继续向上更新
  • 如果parent的平衡因子为正负2,则parent的平衡因子违反平衡树的性质,需要对其进行旋转处理

先看目前我们所能写出来的代码:

        //2、修改平衡因子//循环条件,暂时不看while () {//先看cur是parent的左还是右,决定平衡因子是加1还是减1if(cur == parent.right) {//右树高度增加,平衡因子++parent.bf++;} else {//左树高度增加,平衡因子--parent.bf--;}if(parent.bf == 0) {break;//已经平衡了} else if(parent.bf == 1 || parent.bf == -1) {//需要继续向上检查cur = parent;parent = parent.parent;} else {//需要左/右旋转if(parent.bf == 2) {//右树高----需要降低右树的高度if(parent.bf == 1) {} else {//parent.bf == -1}} else{//parent.bf == -2//左树高----需要降低左树的高度if(parent.bf == -1) {} else {//parent.bf == 1}}}}

接下来,需要做的是,填充各个情况下,是需要将树进行左旋还是右旋


4、AVL树的旋转

4.1、新节点插入较高左子树的左侧——右单旋

流程:

具体实现:

另外p还需要考虑的情况:

代码:

    /*** 右单旋* @param parent*/private void rotateRight(TreeNode parent) {TreeNode subL = parent.left;TreeNode subLR = subL.right;parent.left = subLR;subL.right = parent;//没有subLRif(subLR != null) {subLR.parent = parent;}//记录p的父节点TreeNode pParent = parent.parent;parent.parent = subL;//检查当前是不是根节点if(parent == root) {root = subL;root.parent = null;} else {//不是根节点//判断左右树if(pParent.left == parent) {pParent.left = subL;} else {pParent.right = subL;}subL.parent = pParent;}subL.bf = 0;parent.bf = 0;}

4.2、新节点插入较高右子树的右侧——左单旋

流程:

具体实现:

同上述的右旋,p单独考虑的情况【p也可能会是某个节点的左/右孩子】

代码:

     /*** 左单旋* @param parent*/private void rotateLeft(TreeNode parent) {TreeNode subR = parent.right;TreeNode subRL = subR.left;parent.right = subRL;if(subRL != null) {subRL.parent = parent;}TreeNode pParent = parent.parent;parent.parent = subR;if(root == parent) {root = subR;root.parent = null;} else {if (pParent.left == parent) {parent.left = subR;} else {parent.right = subR;}subR.parent = pParent;}subR.bf = 0;parent.bf = 0;}

4.3、新节点插入较高左子树的右侧——左右双旋

流程:

当插入的值是55的时候,就是插入在50的右边,也是左右双旋,但是最终的平衡因子的更改略有差异,可以自己试着画一画。 

代码:

    /*** 左右双旋* @param parent*/private void rotateLR(TreeNode parent) {TreeNode subL = parent.left;TreeNode subLR = subL.right;int bf = subLR.bf;//左rotateLeft(parent.left);//右rotateRight(parent);if(bf == -1) {subL.bf = 0;subLR.bf = 0;parent.bf = 1;} else if(bf == 1) {subL.bf = -1;subLR.bf = 0;parent.bf = 0;}}

4.4、新节点插入较高右子树的左侧——右左双旋

流程:

当插入的值是40的时候,就是插入在50的左边,也是右左双旋,但是最终的平衡因子的更改略有差异,可以自己试着画一画。 

代码:

    /*** 右左双旋* @param parent*/private void rotateRL(TreeNode parent) {TreeNode subR = parent.right;TreeNode subRL = subR.left;int bf = subRL.bf;rotateRight(parent.right);rotateLeft(parent);if(bf == 1) {parent.bf = -1;subR.bf = 0;subRL.bf = 0;} else {parent.bf = 0;subR.bf = 1;subRL.bf = 0;}}

5、AVL树的验证

同插入类似,由于它是特殊的二叉搜索树,所以验证时,分两步:

  • 验证是否为二叉搜索树
  • 验证是否为平衡树

代码:

    //中序遍历的结果是有序的 就能说明当前树 一定是搜索树public void inorder(TreeNode root) {if(root == null) return;inorder(root.left);System.out.print(root.val+"  ");inorder(root.right);}//计算高度private int height(TreeNode root) {if(root == null) return 0;int leftH = height(root.left);int rightH = height(root.right);return leftH > rightH ? leftH+1 : rightH+1;}//是否高度平衡:public boolean isBalanced(TreeNode root) {if(root == null) return true;int leftH = height(root.left);int rightH = height(root.right);if(rightH-leftH != root.bf) {System.out.println("这个节点:"+root.val+" 平衡因子异常");return false;}return Math.abs(leftH-rightH) <= 1&& isBalanced(root.left)&& isBalanced(root.right);}

代码汇总:

/*** Created with IntelliJ IDEA.* Description:* User:龙宝* Date:2023-01-10* Time:17:38*/
public class AVLTree {static class TreeNode {public int val;public int bf;//平衡因子public TreeNode left;//左孩子引用public TreeNode right;//右孩子引用public TreeNode parent;//父节点引用public TreeNode(int val) {this.val = val;}}public TreeNode root;//根节点/*** 插入新的节点* @param val* @return 插入成功,返回true,失败返回false*/public boolean insert(int val) {//1、按照二叉搜索树的方式插入新的节点if(root == null) {root = new TreeNode(val);return true;}TreeNode node = new TreeNode(val);TreeNode parent = null;TreeNode cur = root;while(cur != null) {if(cur.val < val){parent = cur;cur = cur.right;} else if(cur.val == val) {return false;} else {parent = cur;cur = cur.left;}}if(parent.val < val) {parent.right = node;} else {parent.left = node;}node.parent = parent;cur = node;//2、修改平衡因子while (parent != null) {//先看cur是parent的左还是右,决定平衡因子是加1还是减1if(cur == parent.right) {//右树高度增加,平衡因子++parent.bf++;} else {//左树高度增加,平衡因子--parent.bf--;}if(parent.bf == 0) {break;//已经平衡了} else if(parent.bf == 1 || parent.bf == -1) {//需要继续向上检查cur = parent;parent = parent.parent;} else {if(parent.bf == 2) {//右树高----需要降低右树的高度if(cur.bf == 1) {//左旋rotateLeft(parent);} else {//cur.bf == -1rotateRL(parent);}} else{//parent.bf == -2//左树高----需要降低左树的高度if(cur.bf == -1) {//右旋rotateRight(parent);} else {//cur.bf == 1rotateLR(parent);}}break;}}return  false;}/*** 右单旋* @param parent*/private void rotateRight(TreeNode parent) {TreeNode subL = parent.left;TreeNode subLR = subL.right;parent.left = subLR;subL.right = parent;//没有subLRif(subLR != null) {subLR.parent = parent;}//记录p的父节点TreeNode pParent = parent.parent;parent.parent = subL;//检查当前是不是根节点if(parent == root) {root = subL;root.parent = null;} else {//不是根节点//判断左右树if(pParent.left == parent) {pParent.left = subL;} else {pParent.right = subL;}subL.parent = pParent;}subL.bf = 0;parent.bf = 0;}/*** 左单旋* @param parent*/private void rotateLeft(TreeNode parent) {TreeNode subR = parent.right;TreeNode subRL = subR.left;parent.right = subRL;if(subRL != null) {subRL.parent = parent;}TreeNode pParent = parent.parent;parent.parent = subR;if(root == parent) {root = subR;root.parent = null;} else {if (pParent.left == parent) {parent.left = subR;} else {parent.right = subR;}subR.parent = pParent;}subR.bf = 0;parent.bf = 0;}/*** 左右双旋* @param parent*/private void rotateLR(TreeNode parent) {TreeNode subL = parent.left;TreeNode subLR = subL.right;int bf = subLR.bf;//左rotateLeft(parent.left);//右rotateRight(parent);if(bf == -1) {subL.bf = 0;subLR.bf = 0;parent.bf = 1;} else if(bf == 1) {subL.bf = -1;subLR.bf = 0;parent.bf = 0;}}/*** 右左双旋* @param parent*/private void rotateRL(TreeNode parent) {TreeNode subR = parent.right;TreeNode subRL = subR.left;int bf = subRL.bf;rotateRight(parent.right);rotateLeft(parent);if(bf == 1) {parent.bf = -1;subR.bf = 0;subRL.bf = 0;} else {parent.bf = 0;subR.bf = 1;subRL.bf = 0;}}//验证://中序遍历的结果是有序的 就能说明当前树 一定是AVL树吗?  不一定的public void inorder(TreeNode root) {if(root == null) return;inorder(root.left);System.out.print(root.val+"  ");inorder(root.right);}private int height(TreeNode root) {if(root == null) return 0;int leftH = height(root.left);int rightH = height(root.right);return leftH > rightH ? leftH+1 : rightH+1;}public boolean isBalanced(TreeNode root) {if(root == null) return true;int leftH = height(root.left);int rightH = height(root.right);if(rightH-leftH != root.bf) {System.out.println("这个节点:"+root.val+" 平衡因子异常");return false;}return Math.abs(leftH-rightH) <= 1&& isBalanced(root.left)&& isBalanced(root.right);}}

6、AVL树的删除

同插入类似,先删除节点,在更新平衡因子,不同的是,删除节点后平衡因子的持续更新时,最差情况下需要一直调整到根节点处


7、AVL树性能分析

  • 如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。
  • 如果要AVL树做一些结构修改的操作,性能非常低下,例:插入时要维护绝对平衡,旋转次数较多,特别是在删除的时候,可能会一直让旋转持续到根的位置

好啦,本期结束咯,咱们下期见~

二叉平衡树之AVL树【手动实现代码】相关推荐

  1. 二叉平衡树(AVL树)从演变、平衡、旋转加练习题逐步分析,看不会过来打我

    想要了解AVL树,就得了解它是怎么演化来的,它并不是凭空创造的一个新数据结构,而是发现其他数据结构的不完美而演变过来的. 二叉查找树 我想二叉排序树结构的起源一定是来源于生活,二叉树只有一个根节点,每 ...

  2. 种树:二叉树、二叉搜索树、AVL树、红黑树、哈夫曼树、B树、树与森林

    虽然今天不是植树节,但是我今天想种树. 文章目录 树,什么是树? 二叉树 定义 二叉树的创建 二叉树的前中后序遍历 前序遍历: 中序遍历 后序遍历 已知前序.中序遍历结果,还原二叉树 已知后序.中序遍 ...

  3. 《恋上数据结构第1季》平衡二叉搜索树、AVL树

    AVL树 二叉搜索树缺点分析 改进二叉搜索树 平衡(Balance) 理想平衡 如何改进二叉搜索树? 平衡二叉搜索树(Balanced Binary Search Tree) AVL树 BST 对比 ...

  4. 【算法导论】 二叉搜索树、AVL树、和红黑树

    二叉搜索树 二叉搜索树是一颗二叉树或一颗空树且满足以下性质: 1)根节点 x的key值大于任意左子树上节点的key值,小于右子树上任意节点的key值 : 2)其左右子树也分别是一颗二叉搜索树. 使用二 ...

  5. 入门二叉平衡树的世界

    入门二叉平衡树的世界 1. 二叉平衡树的概念     二叉平衡树又称AVL树,它或者是一棵空二叉树,或者是具有下列性质的二叉树: 1) 根的左右子树高度之差的绝对值不超过1: 2) 根的左右子树都是二 ...

  6. 数据结构与算法——二叉平衡树(AVL树)详解

    文章目录 AVL树概念 不平衡概况 四种平衡旋转方式 RR平衡旋转(左单旋转) LL平衡旋转(右单旋转) RL平衡旋转(先右后左双旋转) LR平衡旋转(先左后右单旋转) java代码实现 总结 AVL ...

  7. AVL树(二叉平衡树)详解与实现

    公众号文章链接 AVL树概念 前面学习二叉查找树和二叉树的各种遍历,但是其查找效率不稳定(斜树),而二叉平衡树的用途更多.查找相比稳定很多.(欢迎关注数据结构专栏) AVL树是带有平衡条件的二叉查找树 ...

  8. 二叉平衡树(AVL树)详细理解

    二叉平衡树(AVL树) AVL树插入元素结论 单旋转: 双旋转: 如果看到后面会发现,我下面举得列子,类型一和类型三和我结论里面的有点不一样,那是因为类型一的节点4和类型三的节点14无论以何种方式都能 ...

  9. C++判断一棵树是否为AVL(二叉平衡树)

    1. 题目要求 判断一棵二叉树是否是平衡二叉树 2. 思路 AVL树的名字来源于它的发明作者G.M. Adelson-Velsky 和 E.M. Landis.AVL树是最先发明的自平衡二叉查找树(S ...

最新文章

  1. TVM量化路线图roadmap
  2. 微信浏览器跳转页面加载loading效果问题
  3. Installshield建立IE快捷方式的方法
  4. 陈中华:李彦宏候选工程院院士,是全中国人民的大耻辱
  5. .net之 datagrid
  6. java拆解_深入拆解Java虚拟机视频教程
  7. EventBridge消息路由|高效构建消息路由能力
  8. 关于Infobright的一个小TIPS
  9. DS汽车通过采用沉浸式虚拟现实技术实现展厅转型
  10. MySQL 学习三:来教你如何完全卸载掉本地“头大的” MySQL 数据库!
  11. php制作学生卡片,PHP基础案例一:展示学生资料卡
  12. 基于Java和Bytemd用120行代码实现一个桌面版Markdown编辑器
  13. matlab求半径范围内的点,matlab怎么快速搜索距离某点球形范围内的所有点
  14. 删除了计算机网络如何恢复,回收站删除了怎么恢复?回收站清空了怎么恢复简单方法【图文】-太平洋电脑网PConline-太平洋电脑网...
  15. 拓扑排序Topological Sorting
  16. cs用服务器运行,如何搭建自己的CS服务器(插件配置篇)
  17. Core Java笔记------来自达内项目经理穆笛
  18. 微信小游戏开发实战教程2-使用表格处理数据
  19. 时钟周期、机器周期、总线周期、指令周期的关系
  20. DB-Engines:2016年10月份全球数据库排名

热门文章

  1. 你羡慕我国庆能出去旅游,我羡慕你加班3倍工资
  2. 为什么企业微信只能群发一次?如何增加群发次数?
  3. 【技能】前端技能列表
  4. 重新定义物流快递如何实现跨行业连接
  5. 你居然不会狄杰斯特算法?惊了!
  6. 对于一个大数据应用项目/产品的落地,可以大致总结为五大步骤阶段?
  7. 企业在项目中采用工时管理系统的好处
  8. 通达oa显示服务器错误,服务器监控
  9. springboot+vue项目部署到外网服务器的完整步骤(前后端分离 分别部署)
  10. 如何快速实现微信账号的注销,福利在这里,怎样快速注销微信账号