什么是AVL树


二叉查找树的一个局限性就是有可能退化成一个链表,这种情况下二叉查找树的效率就会急剧下降变成0(n)。而AVL树可以很好地解决BST的这种困境。本篇博客会介绍AVL树的基本特点和相关操作。

文章参考自博客:二叉树-你可能需要知道的知识点


1. 什么是AVL树

任何两个子树的高度差最大是1,这样的二叉树叫做AVL树。

先来确定几个概念:

平衡因子:将二叉树上节点的左子树高度减去右子树高度的值称为该节点的平衡因子BF(Balance Factor)。

最小不平衡子树:距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树。

左边二叉树的节点45的BF = 1,插入节点43后,节点45的BF=2。

节点45是距离插入点43最近的BF不在[-1,1]范围内的节点,因此以节点45为根的子树为最小不平衡子树。

2. 节点的实现

public class AVLTreeNode<T extends Comparable> {

    // 存储的数据-用于排序
T key; // 节点高度-用于计算父节点的BF
int height; // 左儿子 & 右儿子
AVLTreeNode<T> left;
AVLTreeNode<T> right; public AVLTreeNode() {
} public AVLTreeNode(T key, AVLTreeNode<T> left, AVLTreeNode<T> right) {
this.key = key;
this.left = left;
this.right = right;
this.height = 0;
} }

节点的定义还是比较简单的,相对于之前的定义多了一个height属性用于计算父节点的BF。

树的定义

public class AVLTree<T extends Comparable> {
// 定义树的根节点
AVLTreeNode<T> root; public AVLTree() {
root = null;
}
}

获取树的高度

    public int height() {
return height(root);
} private int height(AVLTreeNode<T> tree) {
if (tree != null){
return tree.height;
}
return 0;
}

本文章将空树的高度定义为0,高度以树的层次为准,根节点的高度为1,依次类推。

3. AVL树的调整

如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种不平衡可能出现在下面四种情况中:

  • 对a的左儿子的左子树进行一次插入。(LL)
  • 对a的左儿子的右子树进行一次插入。(LR)
  • 对a的右儿子的左子树进行一次插入。(RL)
  • 对a的右儿子的右子树进行一次插入。(RR)

    其中1、4是关于a点的镜像对称,2、3是关于a点的镜像对称。

第一种情况(1、4)需要通过对树的一次单旋转完成调整。

第二种情况(2、3)需要通过对树的一次双旋转完成调整。

3.1 LL旋转

在左子树上插入左孩子导致AVL树失衡,"根的左子树的高度"比"根的右子树的高度"大2。针对该情况,我们需要进行单右旋转来完成对树的调整。

图中左边是旋转之前的树,右边是旋转之后的树。从中可以发现,旋转之后的树又变成了AVL树,而且该旋转只需要一次即可完成。

对于LL旋转,你可以这样理解为:LL旋转是围绕"失去平衡的AVL根节点"进行的,也就是节点4;而且由于是LL情况,就将节点4进行一次顺时针旋转。

代码实现:

    /**
* 进行一次单右旋转
*
* @param node 最小失衡树根节点
*/
private AVLTreeNode<T> rightRotation(AVLTreeNode<T> node) {
AVLTreeNode<T> left = node.left;
node.left = left.right;
left.right = node;
// 更新高度
node.height = Math.max(height(node.left), height(node.right)) + 1;
left.height = Math.max(height(left.left), height(left.right)) + 1;
return left;
}

3.2 RR旋转

在右子树插入右孩子导致AVL失衡时,我们需要进行单左旋调整。旋转围绕最小失衡子树的根节点进行。

    /**
* 进行一次单左旋转
*
* @param node 最小失衡树根节点
*/
private AVLTreeNode<T> leftRotation(AVLTreeNode<T> node) {
AVLTreeNode<T> right = node.right;
node.right = right.left;
right.left = node; // 更新高度
node.height = Math.max(height(node.left), height(node.right)) + 1;
right.height = Math.max(height(right.left), height(right.right)) + 1; return right; }

3.3 RL旋转

“在右子树上插入左孩子导致AVL树失衡",此时我们需要进行先右旋后左旋的调整。


/**
* 先右旋后左旋
*
* @param node 失衡树根节点
* @return 旋转后的根节点
*/
private AVLTreeNode<T> rightLeftRotation(AVLTreeNode<T> node) {
node.right = rightRoation(node.right); return leftRoation(node); }

3.4 LR旋转

“在左子树上插入右孩子导致AVL树失衡",此时我们需要进行先左旋后右旋的调整。


/**
* 先左旋后右旋
*
* @param node 失衡树根节点
* @return 旋转后的根节点
*/
private AVLTreeNode<T> leftRightRotation(AVLTreeNode<T> node) {
node.left = leftRoation(node.left); return rightLeftRoation(node); }

【数据结构】什么是AVL树的更多相关文章

  1. 数据结构与算法——AVL树类的C++实现

    关于AVL树的简单介绍能够參考:数据结构与算法--AVL树简单介绍 关于二叉搜索树(也称为二叉查找树)能够參考:数据结构与算法--二叉查找树类的C++实现 AVL-tree是一个"加上了额外 ...

  2. 【数据结构】平衡二叉树—AVL树

    (百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增 ...

  3. 数据结构(三)实现AVL树

    AVL树的定义 一种自平衡二叉查找树,中面向内存的数据结构. 二叉搜索树T为AVL树的满足条件为: T是空树 T若不是空树,则TL.TR都是AVL树,且|HL-HR| <= 1 (节点的左子树高 ...

  4. 数据结构与算法分析-AVL树

    1.AVL树是带有平衡条件的二叉查找树. 2.AVL树的每个节点高度最多相差1. 3.AVL树实现的难点在于插入或删除操作.由于插入和删除都有可能破坏AVL树高度最多相差1的特性,所以当特性被破坏时需 ...

  5. 数据结构——二叉查找树、AVL树

    二叉查找树:由于二叉查找树建树的过程即为插入的过程,所以其中序遍历一定为升序排列! 插入:直接插入,插入后一定为根节点 查找:直接查找 删除:叶子节点直接删除,有一个孩子的节点删除后将孩子节点接入到父 ...

  6. [数据结构与算法] : AVL树

    头文件 typedef int ElementType; #ifndef _AVLTREE_H_ #define _AVLTREE_H_ struct AvlNode; typedef struct ...

  7. AVL树和伸展树 -数据结构(C语言实现)

    读数据结构与算法分析 AVL树 带有平衡条件的二叉树,通常要求每颗树的左右子树深度差<=1 可以将破坏平衡的插入操作分为四种,最后通过旋转恢复平衡 破坏平衡的插入方式 描述 恢复平衡旋转方式 L ...

  8. 数据结构图文解析之:AVL树详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  9. 图解数据结构树之AVL树

    AVL树(平衡二叉树): AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树.在AVL树中任何节点的两个子 ...

  10. 深入浅出数据结构C语言版(12)——平衡二叉查找树之AVL树

    在上一篇博文中我们提到了,如果对普通二叉查找树进行随机的插入.删除,很可能导致树的严重不平衡 所以这一次,我们就来介绍一种最老的.可以实现左右子树"平衡效果"的树(或者说算法),即 ...

随机推荐

  1. 关于c++中的复合类型

    目录 数组 字符串 结构体 共用体 枚举 指针 数和指针的关系 常见的存储方式 数组替代品 一.数组 存储在每个元素中值的类型 数组名 数组中的元素数 通用格式:typename arrayname ...

  2. 为什么不建议在hbase中使用过多的列簇

    我们知道,hbase表可以设置一个至多个列簇(column families),但是为什么说越少的列簇越好呢? 官网原文: HBase currently does not do well with ...

  3. 蔡勒(Zeller)公式及其推导:快速将任意日期转换为星期数

    0. 本文的初衷及蔡勒公式的用处 前一段时间,我在准备北邮计算机考研复试的时候,做了几道与日期计算相关的题目,在这个过程中我接触到了蔡勒公式.先简单的介绍一下蔡勒公式是干什么用的. 我们有时候会遇到这 ...

  4. python实现RSA加密和签名以及分段加解密的方案

    1.前言 很多朋友在工作中,会遇到一些接口使用RSA加密和签名来处理的请求参数,那么遇到这个问题的时候,第一时间当然是找开发要加解密的方法,但是开发给加解密代码,大多数情况都是java,c++,js等 ...

  5. Java基础之访问权限控制

    Java基础之访问权限控制 四种访问权限 Java中类与成员的访问权限共有四种,其中三种有访问权限修饰词:public,protected,private. Public:权限最大,允许所有类访问,但 ...

  6. QFramework 使用指南 2020(三):脚本生成(1)基本使用

    在上一篇,我们对 QFramework 的两个主要版本提供了介绍,并且写下了第一个 QFramework 脚本. 在这一篇,我们学习 QFramework 中几乎每个项目都要用到并且从中受益的功能:自 ...

  7. 洛谷 P1262 【间谍网络】

    题库 : 洛谷 题号 : 1262 题目 : 间谍网络 link : https://www.luogu.org/problemnew/show/P1262 思路 : 这题可以用缩点的思想来做.先用T ...

  8. SpringCloud(二)- 服务注册与发现Eureka

    离上一篇微服务的基本概念已经过去了几个月,在写那篇博客之前,自己还并未真正的使用微服务架构,很多理解还存在概念上.后面换了公司,新公司既用了SpringCloud也用了Dubbo+Zookeeper, ...

  9. ValueError: Error when checking input: expected input_1 to have 2 dimensions, but got array with shape (100, 100, 100, 3)

    报错 Traceback (most recent call last): File "D:/PyCharm 5.0.3/WorkSpace/3.Keras/1.Sequential与Mod ...

  10. 微服务时代之网关相关技术选型及部署(nacos+gateway)

    1.场景描述 因要用到微服务,关于注册中心这块,与同事在技术原型上做了讨论,初步定的方案是使用:阿里巴巴的nacos+springcloud gateway,下面表格是同事整理的注册中心对比,以前用的 ...