平衡二叉树(AVL Tree)

概述

AVL树是以二分搜索树(BST)为底层数据结构而实现的,其特性是需要维护AVL的|平衡因子| <= 1

平衡因子
对于一个父节点的左右子树高度差的绝对值需要 <= 1

AVL最复杂的操作在于add()remove()两个方法需要维护AVL的平衡二叉树特性,细节在下面展开

节点类

由于需要知道AVL一个节点的高度,因此需要在BST的内部类Node中新维护一个成员变量height

public class Node<K, V> {K key;V value;int height;Node<K, V> left, right;public Node(K key, V value) {this.key = key;this.value = value;this.left = null;this.right = null;this.height = 1;}
}

IMap接口

该接口定义了BST的方法

public interface IMap<K, V> {void add(K key, V value);V remove(K key);boolean contains(K key);V get(K key);void set(K key, V value);int getSize();boolean isEmpty();
}

AVLTree类

成员变量以及构造方法

private int size;
private Node<K, V> root;public AVLTree() {root = null;size = 0;
}

基本的方法

下面的方法均是为了实现add()remove()方法时,可能造成AVL的平衡性被打破而封装的辅助方法

  • getHeight()
private int getHeight(Node<K, V> node) {if (node == null) {return 0;}return node.height;
}

该方法用于获取某节点的高度,且维护nodenull时可能抛出的异常

  • getNode(Node node, K key)
private Node<K, V> getNode(Node<K, V> node, K key) {if (root == null) {return null;}if (key.compareTo(node.key) == 0) {return node;} else if (key.compareTo(node.key) < 0) {return getNode(node.left, key);} else {return getNode(node.right, key);}
}

根据key获取节点

  • getBalanceFactor(Node node)
// 获取平衡因子
private int getBalanceFactor(Node<K, V> node) {if (node == null) {return 0;}return getHeight(node.left) - getHeight(node.right);
}

获取平衡因子,并且维护了node可能为null而抛出异常的情况

核心方法

  • add(Node node)
@Override
public void add(K key, V value) {root = add(root, key, value);
}private Node<K, V> add(Node<K, V> node, K key, V value) {if (node == null) {size++;return new Node<>(key, value);}if (key.compareTo(node.key) < 0) {node.left = add(node.left, key, value);} else if (key.compareTo(node.key) > 0) {node.right = add(node.right, key, value);} else {// 如果插入节点存在,此处定义为改变 Value 的值(也可以根据情况不做改变)node.value = value;}node.height = Math.max(getHeight(node.left), getHeight(node.right) + 1);int balanceFactor = getBalanceFactor(node);// 平衡维护// LLif (balanceFactor > 1 && getBalanceFactor(node.left) >= 0) {return rightRotate(node);}// RRif (balanceFactor < -1 && getBalanceFactor(node.right) <= 0) {return leftRotate(node);}// LRif (balanceFactor > 1 && getBalanceFactor(node.right) < 0) {node.left = leftRotate(node.left);return rightRotate(node);}// RLif (balanceFactor < -1 && getBalanceFactor(node.right) > 0) {node.right = leftRotate(node);return rightRotate(node.right);}return node;
}

  • remove(K key)
@Override
public V remove(K key) {Node<K, V> node = getNode(root, key);if (node != null) {root = remove(root, key);return node.value;}return null;
}private Node<K, V> remove(Node<K, V> node, K key) {if (node == null) {return null;}Node<K, V> retNode;if (key.compareTo(node.key) < 0) {node.left = remove(node.left, key);retNode = node;} else if (key.compareTo(node.key) > 0) {node.right = remove(node.right, key);retNode = node;} else {if (node.left == null) {Node<K, V> rightNode = node.right;node.right = null;size--;retNode = node;}if (node.right == null) {Node<K, V> leftNode = node.left;node.left = null;size--;retNode = node;}// 左右子树均不为空的情况Node<K, V> minimum = minimum(node.right);minimum.right = removeMin(node.right);minimum.left = node.left;node.left = node.right = null;retNode = minimum;}retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));int balanceFactor = getBalanceFactor(retNode);// 平衡维护// LLif (balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0) {return rightRotate(retNode);}// RRif (balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0) {return leftRotate(retNode);}// LRif (balanceFactor > 1 && getBalanceFactor(retNode.right) < 0) {retNode.left = leftRotate(retNode.left);return rightRotate(retNode);}// RLif (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0) {retNode.right = leftRotate(retNode);return rightRotate(retNode.right);}return retNode;
}

维护AVL的平衡性质——旋转

这里是文章的核心内容,即如何保证AVL的平衡性质,这里开始详细描述维护AVL特性的机制——旋转

旋转分为四种形式,分别为:

  • LL旋转
  • RR旋转
  • LR旋转
  • RL旋转

只需要理解一种旋转机制,就可以理解其它的旋转机制

这里以LL和LR旋转为例

LL旋转

  • 如图所示,根节点的平衡因子为2,而破坏该AVL的节点在根节点的左子树左节点,因此需要LL旋转来维护其平衡
  • 接下来根据实际代码进一步理解这个机制
  • 为了结合代码,现在虚拟出三个节点(或子树),它们可以是null,也可以实际存在
  • 目前由于是LL旋转,那么提供一个private方法用于维护平衡,下面给出方法详细代码,代码后有每一个表达式的详细解释
// 右旋转
private Node<K, V> rightRotate(Node<K, V> y) {Node<K, V> x = y.left;Node<K, V> t3 = x.right;x.right = y;y.left = t3;// 更新heighty.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;return x;
}

  • 第一行表达式Node<K, V> x = y.left,将发现节点y的左子树暂存
  • 第二行表达式Node<K, V> t3 = x.right,将单独拿出的节点的右子树t3暂存
  • 第三行表达式x.right = y,将发现节点y接入x的右子树
  • 第四行表达式y.left = t3,将暂存节点t3插入发现节点y的左节点
  • 注意:以上的每个操作都是满足了二分搜索树(BST)的特性,这点对于理解旋转机制很重要
  • 由于最终平衡后的AVL高度发生了改变,因此需要对更新后的AVL高度进行维护
// 更新height
y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;

LR旋转

LR旋转和LL旋转的差别在于LR旋转需要进行两次旋转操作,具体步骤如下:

  • 首先对于上面的AVL进行一次左旋转,然后就会暂时将其转变为LL旋转的状态
  • 然后再进行一次右旋转,即可恢复到AVL的状态

易错点分析

AVL有一个非常容易误解的地方,如图

实际上旋转的命名(LL、RR、LR、RL)也都是根据破坏节点t1(t2)发现节点y的相对位置命名

也就是说无论是:

还是:

旋转方式是相同的,均为LL旋转,结果如图:

平衡二叉树平衡因子怎么计算_平衡二叉树(AVL Tree)旋转机制分析相关推荐

  1. 平衡二叉树平衡因子怎么计算_数据结构PHP 平衡二叉树(AVL)的平衡原理

    这篇文章主要介绍一下 平衡二叉树(AVL),对于 二分搜索树 来说,如果树上的 元素 是顺序 添加的,会导致数据退化成一个 链表,这样就会造成很严重的性能问题,此时就需要在 二分搜索树 的基础上,保证 ...

  2. 平衡二叉树平衡因子_数据结构:平衡二叉树

    1.基本概念    平衡二叉树(AVL树),或为空树,或为如下性质的二叉排序树:左右子树深度之差的绝对值不超过1;左右子树仍然为平衡二叉树. 平衡因子BF=左子树深度-右子树深度. 平衡二叉树每个结点 ...

  3. 板式橡胶支座弹性模量怎样计算_板式橡胶支座抗压弹性模量试验分析(详细)

    板式橡胶支座抗压弹性模量试验分析 1. 概述近年来我国交通事业发展迅速 , 桥梁作为我国重要社会基础设施的 地位愈显突出 , 在国民经济和居民日常生活中发挥着重要作用 . 桥梁支座 是桥粱结构的 重要 ...

  4. 流式凋亡率计算_流式细胞仪检测细胞凋亡率案例分析报告

    一.实验材料 NCI-H1299,购自上海晶莱生物技术有限公司. 表 2.1.1 主要试剂试剂与耗材厂家(货号) 细胞培养瓶FALCON 中国(353014) Penicillin/streptomy ...

  5. c语言求平衡因子,平衡二叉树(AVL树)的基本操作

    0x00.平衡二叉树的定义 平衡二叉树(AVL树)是一种特殊的二叉搜索树,只是在二叉搜索树上增加了对"平衡"的需求. 假如一棵二叉搜索树,按照"1,2,3,4,5&quo ...

  6. 高度平衡二叉树的构建_平衡二叉树(AVL)树

    1.平衡二叉树定义 是一种二叉排序树(二叉查找树.二叉搜索树),其中每个节点的左子树和右子树的高度差不大于1.(左右子树也是平衡二叉树) 平衡因子BF = 二叉树节点的左子树深度减去右子树深度 = 节 ...

  7. [转]C#与数据结构--树论--平衡二叉树(AVL Tree)

    C#与数据结构--树论--平衡二叉树(AVL Tree) http://www.cnblogs.com/abatei/archive/2008/11/17/1335031.html 介绍 我们知道在二 ...

  8. ds查找—二叉树平衡因子_面试官让我手写一个平衡二叉树,我当时就笑了

    平衡二叉树对于初学者一直是一个比较复杂的知识点,因为其里面涉及到了大量的旋转操作.把大量的同学都给转晕了.这篇文章最主要的特点就是通过动画的形式演示.确保大家都能看懂.最后是手写一个平衡二叉树. 一. ...

  9. 平衡二叉树及其操作实现_平衡二叉树(AVL树)及C语言实现

    上一节介绍如何使用二叉排序树实现动态查找表,本节介绍另外一种实现方式--平衡二叉树.平衡二叉树,又称为 AVL 树.实际上就是遵循以下两个特点的二叉树: 每棵子树中的左子树和右子树的深度差不能超过 1 ...

最新文章

  1. 查询mysql上传大小限制_解决数据库phpmyadmin中上传最大限制:2,048 KB
  2. 简述python在量化金融中应用_Python金融与量化投资分析应用
  3. 同步、异步事件循环(宏任务、微任务「大厂真题解析」)
  4. SpringMCV整合配置文件
  5. zotero 相关文章链接 (侵权请联系,立即删除)
  6. Android - UI
  7. 用PS给证件照换底色
  8. SyntaxError: Non-UTF-8 code starting with ‘\xd5‘ in file
  9. 概要设计的过程和任务
  10. 515. 在每个树行中找最大值(中等 树 广度优先搜索 二叉树)
  11. 快速的在Adobe Illustrator中创建羊驼插图教程
  12. win10中conda activate激活环境出错的解决办法
  13. 熬夜肝了万字Android View 知识体系
  14. 简单快速清理 联想电脑 顽固可疑程序文件 comup.dll(风险名称: Adware/Hyideo )的方法 PS:该方法对于删除 .dll 文件均有效
  15. linux DNS jnl ixfr axfr 小解
  16. 【滤波】基于最近邻算法实现多目标航迹关联附matlab代码
  17. DirectX12(D3D12)基础教程(十七)——让小姐姐翩翩起舞(3D骨骼动画渲染【6】)
  18. aac是什么音频格式?aac转化为mp3方法
  19. 智慧养老解决方案探索养老行业新方向-新导智能
  20. 走x86路线的海光如今怎么样了?

热门文章

  1. Spring入门详细教程(四)
  2. linux文件权限_Linux的文件特殊权限
  3. cuda 安装_win10+VS 2017 安装 CUDA(Visual Studio Integration失败)
  4. Selenium之XPATH轴定位(第三篇)
  5. mysql 过程和函数 变量的值_MySQL数据库提升篇-----存储过程和函数
  6. php贺卡生成,用php与mysql的电子贺卡程序
  7. wxpython应用程序对象与顶级窗口_wxPython 基础 | 学步园
  8. android小程序源代码_我从 Android 转到微信小程序的思考
  9. spark sql 查看分区_Spark 3.0 中七个必须知道的 SQL 性能优化
  10. laravel 5.5 顶部带条件分页查询