我的编程空间,编程开发者的网络收藏夹
学习永远不晚

什么是平衡二叉树AVL

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

什么是平衡二叉树AVL

什么是平衡二叉树AVL,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

前言

Wiki:在计算机科学中,AVL树是最早被发明的自平衡二叉查找树。在AVL树中,任一节点对应的两棵子树的最大高度差为1,因此它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下的时间复杂度都是 O(logn)。增加和删除元素的操作则可能需要借由一次或多次树旋转,以实现树的重新平衡。AVL 树得名于它的发明者 G. M. Adelson-Velsky 和 Evgenii Landis,他们在1962年的论文《An algorithm for the organization of information》中公开了这一数据结构。

1 为什么要有平衡二叉树

二叉搜索树一定程度上可以提高搜索效率,但是当原序列有序时,例如序列 A = {1,2,3,4,5,6},构造二叉搜索树如图 1.1。依据此序列构造的二叉搜索树为右斜树,同时二叉树退化成单链表,搜索效率降低为 O(n)。

什么是平衡二叉树AVL

在此二叉搜索树中查找元素 6 需要查找 6 次。

二叉搜索树的查找效率取决于树的高度,因此保持树的高度最小,即可保证树的查找效率。同样的序列 A,将其改为图 1.2 的方式存储,查找元素 6 时只需比较 3 次,查找效率提升一倍。

什么是平衡二叉树AVL

可以看出当节点数目一定,保持树的左右两端保持平衡,树的查找效率最高。

这种左右子树的高度相差不超过 1 的树为平衡二叉树。

2. 定义

平衡二叉查找树:简称平衡二叉树。由前苏联的数学家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉树,根据科学家的英文名也称为 AVL 树。它具有如下几个性质:

  1. 可以是空树。

  2. 假如不是空树,任何一个节点的左子树与右子树都是平衡二叉树,并且高度之差的绝对值不超过 1。

平衡之意,如天平,即两边的分量大约相同。

例如图 2.1 不是平衡二叉树,因为节点 60 的左子树不是平衡二叉树。

什么是平衡二叉树AVL

图 2.2 也不是平衡二叉树,因为虽然任何一个节点的左子树与右子树都是平衡二叉树,但高度之差已经超过 1 。

什么是平衡二叉树AVL

图 2.3 是平衡二叉树。

什么是平衡二叉树AVL

3. 平衡因子

定义:某节点的左子树与右子树的高度(深度)差即为该节点的平衡因子(BF,Balance Factor),平衡二叉树中不存在平衡因子大于 1 的节点。在一棵平衡二叉树中,节点的平衡因子只能取 0 、1 或者 -1 ,分别对应着左右子树等高,左子树比较高,右子树比较高。

什么是平衡二叉树AVL
什么是平衡二叉树AVL
什么是平衡二叉树AVL

4. 节点结构

定义平衡二叉树的节点结构:

typedef struct AVLNode *Tree;

typedef int ElementType;

struct AVLNode{

    int depth; //深度,这里计算每个节点的深度,通过深度的比较可得出是否平衡

    Tree parent; //该节点的父节点

    ElementType val; //节点值

    Tree lchild;

    Tree rchild;

    AVLNode(int val=0) {
        parent = NULL;
        depth = 0;
        lchild = rchild = NULL;
        this->val=val;
    }
};

5. AVL树插入时的失衡与调整

图 5.1 是一颗平衡二叉树

什么是平衡二叉树AVL

在此平衡二叉树插入节点 99 ,树结构变为:

什么是平衡二叉树AVL

在动图 5.2 中,节点 66 的左子树高度为 1,右子树高度为 3,此时平衡因子为 -2,树失去平衡。

在动图 5.2 中,以节点 66 为父节点的那颗树就称为 最小失衡子树

最小失衡子树:在新插入的节点向上查找,以第一个平衡因子的绝对值超过 1 的节点为根的子树称为最小不平衡子树。也就是说,一棵失衡的树,是有可能有多棵子树同时失衡的。而这个时候,我们只要调整最小的不平衡子树,就能够将不平衡的树调整为平衡的树。

平衡二叉树的失衡调整主要是通过旋转最小失衡子树来实现的。根据旋转的方向有两种处理方式,左旋右旋

旋转的目的就是减少高度,通过降低整棵树的高度来平衡。哪边的树高,就把那边的树向上旋转。

5.1 左旋
什么是平衡二叉树AVL

以图 5.1.1 为例,加入新节点 99 后, 节点 66 的左子树高度为 1,右子树高度为 3,此时平衡因子为 -2。为保证树的平衡,此时需要对节点 66 做出旋转,因为右子树高度高于左子树,对节点进行左旋操作,流程如下:

(1)节点的右孩子替代此节点位置
(2)右孩子的左子树变为该节点的右子树
(3)节点本身变为右孩子的左子树

整个操作流程如动图 5.1.2 所示。

什么是平衡二叉树AVL
  • 节点的右孩子替代此节点位置 —— 节点 66 的右孩子是节点 77 ,将节点 77 代替节点 66 的位置

  • 右孩子的左子树变为该节点的右子树 —— 节点 77 的左子树为节点 75,将节点 75 挪到节点 66 的右子树位置

  • 节点本身变为右孩子的左子树 —— 节点 66 变为了节点 77 的左子树

5.2 右旋

右旋操作与左旋类似,操作流程为:

(1)节点的左孩子代表此节点
(2)节点的左孩子的右子树变为节点的左子树
(3)将此节点作为左孩子节点的右子树。

什么是平衡二叉树AVL

6. AVL树的四种插入节点方式

假设一颗 AVL 树的某个节点为 A,有四种操作会使 A 的左右子树高度差大于 1,从而破坏了原有 AVL 树的平衡性。平衡二叉树插入节点的情况分为以下四种:

什么是平衡二叉树AVL

具体分析如下:

6.1 A的左孩子的左子树插入节点(LL)

只需要执行一次右旋即可。

什么是平衡二叉树AVL

实现代码如下:

//LL型调整函数
//返回:新父节点
Tree LL_rotate(Tree node){
    //node为离操作节点最近的失衡的节点
    Tree parent=NULL,son;
    //获取失衡节点的父节点
    parent=node->parent;
    //获取失衡节点的左孩子
    son=node->lchild;
    //设置son节点右孩子的父指针
    if (son->rchild!=NULL)  son->rchild->parent=node;
    //失衡节点的左孩子变更为son的右孩子
    node->lchild=son->rchild;
    //更新失衡节点的高度信息
    update_depth(node);
    //失衡节点变成son的右孩子
    son->rchild=node;
    //设置son的父节点为原失衡节点的父节点
    son->parent=parent;
    //如果失衡节点不是根节点,则开始更新父节点
    if (parent!=NULL){
        //如果父节点的左孩子是失衡节点,指向现在更新后的新孩子son
        if (parent->lchild==node){
            parent->lchild=son;
        }else{
             //父节点的右孩子是失衡节点
              parent->rchild=son;
        }
     }
    //设置失衡节点的父亲
    node->parent=son;
    //更新son节点的高度信息
    update_depth(son);
    return son;
}
6.2 A的右孩子的右子树插入节点(RR)

只需要执行一次左旋即可。

什么是平衡二叉树AVL

实现代码如下:

//RR型调整函数
//返回新父节点
Tree RR_rotate(Tree node){
    //node为离操作节点最近的失衡的节点
    Tree parent=NULL,son;
    //获取失衡节点的父节点
    parent=node->parent;
    //获取失衡节点的右孩子
    son=node->rchild;
    //设置son节点左孩子的父指针
    if (son->lchild!=NULL){
          son->lchild->parent=node;
    }
    //失衡节点的右孩子变更为son的左孩子
    node->rchild=son->lchild;
    //更新失衡节点的高度信息
    update_depth(node);
    //失衡节点变成son的左孩子
    son->lchild=node;
    //设置son的父节点为原失衡节点的父节点
    son->parent=parent;
    //如果失衡节点不是根节点,则开始更新父节点
    if (parent!=NULL){
        //如果父节点的左孩子是失衡节点,指向现在更新后的新孩子son
        if (parent->lchild==node){
            parent->lchild=son;
        }else{
            //父节点的右孩子是失衡节点
            parent->rchild=son;
        } 
    }
    //设置失衡节点的父亲
    node->parent=son;
    //更新son节点的高度信息
    update_depth(son);
    return son;
}
6.3 A的左孩子的右子树插入节点(LR)

若 A 的左孩子节点 B 的右子树 E 插入节点 F ,导致节点 A 失衡,如图:

什么是平衡二叉树AVL

A 的平衡因子为 2 ,若仍按照右旋调整,则变化后的图形为这样:

什么是平衡二叉树AVL

经过右旋调整发现,调整后树仍然失衡,说明这种情况单纯的进行右旋操作不能使树重新平衡。那么这种插入方式需要执行两步操作,使得旋转之后为 原来根节点的左孩子的右孩子作为新的根节点

(1)对失衡节点 A 的左孩子 B 进行左旋操作,即上述 RR 情形操作。
(2)对失衡节点 A 做右旋操作,即上述 LL 情形操作。

调整过程如下:

什么是平衡二叉树AVL
什么是平衡二叉树AVL

也就是说,经过这两步操作,使得 原来根节点的左孩子的右孩子 E 节点成为了新的根节点

代码实现:

//LR型,先左旋转,再右旋转
//返回:新父节点
Tree LR_rotate(Tree node){
    RR_rotate(node->lchild);
    return LL_rotate(node);
}
6.4 A的右孩子的左子树插入节点(RL)

右孩子插入左节点的过程与左孩子插入右节点过程类似,也是需要执行两步操作,使得旋转之后为 原来根节点的右孩子的左孩子作为新的根节点

(1)对失衡节点 A 的右孩子 C 进行右旋操作,即上述 LL 情形操作。
(2)对失衡节点 A 做左旋操作,即上述 RR 情形操作。

什么是平衡二叉树AVL
什么是平衡二叉树AVL
什么是平衡二叉树AVL

也就是说,经过这两步操作,使得 原来根节点的右孩子的左孩子 D 节点成为了新的根节点

代码实现:

//RL型,先右旋转,再左旋转
//返回:新父节点
Tree RL_rotate(Tree node){
    LL_rotate(node->rchild);
    return RR_rotate(node);
}

补充

上述四种插入方式的代码实现的辅助代码如下:

//更新当前深度
void update_depth(Tree node){
    if (node==NULL){
        return;
    }else{
        int depth_Lchild=get_balance(node->lchild); //左孩子深度
        int depth_Rchild=get_balance(node->rchild); //右孩子深度
        node->depth=max(depth_Lchild,depth_Rchild)+1;
    }
}

//获取当前节点的深度
int get_balance(Tree node){
    if (node==NULL){
         return 0;
    }
    return node->depth;
}

//返回当前平衡因子
int is_balance(Tree node){
    if (node==NULL){
         return 0;
    }else{
         return get_balance(node->lchild)-get_balance(node->rchild); 
    }
}
6.5 小总结
  1. 在所有的不平衡情况中,都是按照先 寻找最小不平衡树,然后 寻找所属的不平衡类别,再 根据 4 种类别进行固定化程序的操作

  2. LL , LR ,RR ,RL其实已经为我们提供了最后哪个节点作为新的根指明了方向。如 LR 型最后的根节点为原来的根的左孩子的右孩子,RL 型最后的根节点为原来的根的右孩子的左孩子。只要记住这四种情况,可以很快地推导出所有的情况。

  3. 维护平衡二叉树,最麻烦的地方在于平衡因子的维护。建议读者们根据小吴提供的图片和动图,自己动手画一遍,这样可以更加感性的理解操作。

7. AVL树的四种删除节点方式

AVL 树和二叉查找树的删除操作情况一致,都分为四种情况:

(1)删除叶子节点
(2)删除的节点只有左子树
(3)删除的节点只有右子树
(4)删除的节点既有左子树又有右子树

只不过 AVL 树在删除节点后需要重新检查平衡性并修正,同时,删除操作与插入操作后的平衡修正区别在于,插入操作后只需要对插入栈中的弹出的第一个非平衡节点进行修正,而删除操作需要修正栈中的所有非平衡节点。

删除操作的大致步骤如下:

  • 以前三种情况为基础尝试删除节点,并将访问节点入栈。

  • 如果尝试删除成功,则依次检查栈顶节点的平衡状态,遇到非平衡节点,即进行旋转平衡,直到栈空。

  • 如果尝试删除失败,证明是第四种情况。这时先找到被删除节点的右子树最小节点并删除它,将访问节点继续入栈。

  • 再依次检查栈顶节点的平衡状态和修正直到栈空。

对于删除操作造成的非平衡状态的修正,可以这样理解:对左或者右子树的删除操作相当于对右或者左子树的插入操作,然后再对应上插入的四种情况选择相应的旋转就好了。

7.1 删除叶子节点

处理步骤:

①、将该节点直接从树中删除;

②、其父节点的子树高度的变化将导致父节点平衡因子的变化,通过向上检索并推算其父节点是否失衡;

③、如果其父节点未失衡,则继续向上检索推算其父节点的父节点是否失衡…如此反复②的判断,直到根节点 ;如果向上推算过程中发现了失衡的现象,则进行 ④ 的处理;

④、如果其父节点失衡,则判断是哪种失衡类型 [LL、LR、RR、RL] ,并对其进行相应的平衡化处理。如果平衡化处理结束后,发现与原来以父节点为根节点的树的高度发生变化,则继续进行 ② 的检索推算;如果与原来以父节点为根节点的高度一致时,则可说明父节点的父节点及祖先节点的平衡因子将不会有变化,因此可以退出处理;

什么是平衡二叉树AVL

具体数字演示:

什么是平衡二叉树AVL
7.2  & 7.3 删除的节点只有左子树或右子树

处理步骤:

①、将左子树(右子树)替代原有节点 C 的位置;

②、节点  C 被删除后,则以 C 的父节点  B 为起始推算点,依此向上检索推算各节点(父、祖先)是否失衡;

③、如果其父节点未失衡,则继续向上检索推算其父节点 的父节点 是否失衡…如此反复 ② 的判断,直到根节点 ;如果向上推算过程中发现了失衡的现象,则进行 ④ 的处理;

④、如果其父节点失衡,则判断是哪种失衡类型 [LL、LR、RR、RL] ,并对其进行相应的平衡化处理。如果平衡化处理结束后,发现与原来以父节点为根节点的树的高度发生变化,则继续进行 ② 的检索推算;如果与原来以父节点为根节点的高度一致时,则可说明父节点的父节点及祖先节点的平衡因子将不会有变化,因此可以退出处理;

什么是平衡二叉树AVL
7.4 删除的节点既有左子树又有右子树

处理步骤:

①、找到被删节点 B 和替代节点 BLR (节点 B 的前继节点或后继节点 —— 在此选择 前继);

②、将替代节点 BLR 的值赋给节点 B ,再把替代节点 BLR 的左孩子 BLRL 替换替代节点 BLR 的位置;

③、以 BLR 的父节点 BL 为起始推算点,依此向上检索推算父节点或祖先节点是否失衡;

④、如果其父节点未失衡,则继续向上检索推算其父节点的父节点是否失衡…如此反复③的判断,直到根节点;如果向上推算过程中发现了失衡的现象,则进行⑤的处理;

⑤、如果其父节点失衡,则判断是哪种失衡类型  [LL、LR、RR、RL]  ,并对其进行相应的平衡化处理。如果平衡化处理结束后,发现与原来以父节点为根节点的树的高度发生变化,则继续进行 ② 的检索推算;如果与原来以父节点为根节点的高度一致时,则可说明父节点的父节点及祖先节点的平衡因子将不会有变化,因此可以退出处理;

什么是平衡二叉树AVL

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注编程网行业资讯频道,感谢您对编程网的支持。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

什么是平衡二叉树AVL

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

什么是平衡二叉树AVL

什么是平衡二叉树AVL,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。前言Wiki:在计算机科学中,AVL树是最早被发明的自平衡二叉查找树。在AVL树中,任一节点
2023-06-04

详细理解平衡二叉树AVL与Python实

上一篇文章讨论的二叉搜索树,其时间复杂度最好的情况下是O(log(n)),但是最坏的情况是O(n),什么时候是O(n)呢?像这样:如果先插入10,再插入20,再插入30,再插入40就会成上边这个样子这个就像是双向链表,我们期望它是下面这个样
2023-01-30

Java中平衡二叉树的原理是什么

Java中平衡二叉树的原理是什么?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、平衡二叉树的定义平衡二叉树是一种二叉排序树,其中每一个节点的左子树和右子树的高
2023-06-15

Python树表查找(二叉排序树、平衡二叉树)

本文并不会深入讲解树数据结构的基本的概念,仅是站在使用的角度说清楚动态查询。阅读此文之前,请预备一些树的基础知识。
2023-01-07

C++怎么实现平衡二叉树

本篇内容介绍了“C++怎么实现平衡二叉树”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!平衡二叉树Given a binary tree, d
2023-06-20

Java平衡二叉树怎么实现

本篇内容主要讲解“Java平衡二叉树怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java平衡二叉树怎么实现”吧!什么是二叉搜索树简单来说,就是方便搜索的二叉树,是一种具备特定结构的二叉
2023-06-29

PHP怎么判断是否为平衡二叉树

本篇内容介绍了“PHP怎么判断是否为平衡二叉树”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在二叉树中,有一种叫做平衡二叉树。今天我们就来介
2023-06-20

Java平衡二叉树实例分析

这篇文章主要讲解了“Java平衡二叉树实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java平衡二叉树实例分析”吧!AVL树的引入搜索二叉树有着极高的搜索效率,但是搜索二叉树会出现以
2023-06-30

Java如何实现平衡二叉树

小编给大家分享一下Java如何实现平衡二叉树,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1 平衡二叉树的概述为了避免极端情况下二叉搜索树退化为链表,影响查找效率
2023-06-28

如何使用Java的平衡二叉树

这篇文章主要讲解了“如何使用Java的平衡二叉树”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何使用Java的平衡二叉树”吧!二叉排序树可能的问题给定一个数列{1,2,3,4,5,6},要
2023-06-15

C语言之平衡二叉树详解

平衡二叉树是具有平衡属性的有序二叉树,本文主要介绍了C语言中的平衡二叉树,具有一定的参考价值,需要的小伙伴可以参考阅读
2023-05-17

完全二叉树和线索二叉树是什么

完全二叉树和线索二叉树是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。完全二叉树什么叫完全二叉树呢?在说到完全二叉树之前,我们先说另外一个名词:“满二叉树”。像我们之前
2023-06-20

C语言平衡二叉树问题怎么解决

这篇文章主要介绍“C语言平衡二叉树问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C语言平衡二叉树问题怎么解决”文章能帮助大家解决问题。一、题目描述给定一个二叉树,判断它是否是高度平衡的二
2023-06-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录