数据结构-平衡二叉树——对二叉搜索树的优化

什么是二叉搜索树:数据结构-从二分查找到二叉排序树-CSDN博客

二叉平衡树/平衡二叉树(Balanced Binary Tree ,即BBST),也名AVL树(为纪念提出这个结构的两人)。

平衡二叉树产生的由来

二叉平衡树本质上是对二叉搜索树的优化。由于二叉搜索树在构建过程中会出现左斜 和右斜的情况(这是由于二叉搜索树树构建方式不唯一导致的)。如下图中对于数据 {1,2,3,4,5} 我们可以构建如下三棵树,但是三棵树的搜索效率却有极大差别。

而构建方式的差异会导致我们的搜索效率从O(logn)退化到O(n) 。这不是我们希望看到的结果。因此G. M. Adelson-Velsky和E. M. Landis(两个苏联人吧我记得是)提出了平衡二叉树 ,即AVL树。

基本概念

平衡二叉树在原有的二叉搜索树的概念下,引入了如下概念和定义:

平衡因子(Balanced Factor , 又写为BF , bf):BF = 节点的左子树深度 - 节点的右子树深度

对于一颗平衡二叉树而言,平衡因子的值应当为 -1 , 1 ,平衡因子超出 -1 和 1 的就不是平衡二叉树。

如下图

上面三个就是平衡二叉树,而下面两个就不是平衡二叉树( 字比较丑,体谅一下**)**。

如何将二叉搜索树变为平衡二叉树

教科书给出的方法为以下四种(但在实际情况下这四种情况比较困难)。

很多文章和博客没有仔细讲解左旋和右旋的定义,导致我们看旋转过程非常懵逼(也可能他们也不太清楚),我这里给出左旋和右旋的定义。

记住:左旋和右旋是相对于某个节点来说的

记住:左旋和右旋是相对于某个节点来说的

记住:左旋和右旋是相对于某个节点来说的

左旋的定义

左旋是指将当前节点的父节点作为当前节点的左孩子,同时将当前节点作为原爷爷节点的孩子。

若当前节点已有左孩子,把左孩子(左子树)拆下来等旋转完成后重新插入

右旋的定义

右旋是指将当前节点的父节点作为当前节点的右孩子,同时将当前节点作为原爷爷节点的孩子。

若当前节点已有右孩子,将右孩子(右子树)拆下来,等右旋完成后重新插入

左左型(LL)

对于这种情况,我们需要对这棵树(或子树)进行右旋。

进行右旋后,该树的根节点BF值从 2 变为 0,也就是说,在我们完成右旋操作后,将一棵二叉搜索树变为了二叉平衡树。

右右型(RR)

对于这种情况,我们需要对树(子树)进行左旋

进行左旋后,根节点平衡因子由 -2 变为 0

左右型(LR)

左右型是指在当前节点的左孩子的右方插入节点导致树不平衡

我们需要进行两次旋转------先左旋后右旋。

通过先左旋后右旋我们将二叉搜索树变为了二叉平衡树。

右左型(RL)

右左型是指在当前节点的右孩子的左方插入节点导致树不平衡

我们需要先右旋后左旋来平衡树

这样我们就平衡了二叉搜索树。

综合实现

当我们插入节点时,我们需要沿着插入节点的地方一路向上搜素知道找到第一个BF不在 -1 , 1 中的节点,然后开始旋转。

比如我们按照 {1 , 2 , 3 , 4 , 5 , 6} 这个数值开始创建,每插入一次就检查一次,以保证二叉搜索树的平衡。

·

简单代码实现

这里我只写左旋和右旋的代码了,剩下的地方和二叉搜索树区别不大

定义平衡二叉树

cpp 复制代码
typedef struct BBSTNode {
    int data;
    int count;
    struct BBSTNode* left;
    struct BBSTNode* right;
    struct BBSTNode* parent;
}BBSTNode;

左旋代码

cpp 复制代码
void RotateLeft(BBSTNode *target){
    BBSTNode *curr = target;
    //分情况判断 有无左子树
    if(curr -> left == NULL){
        curr -> left = target;
        if(target -> left == curr) target -> left = NULL;
        else if(target -> right == curr) target -> right = NULL;
        curr -> parent = target -> parent;
        target -> parent = curr;
    }else{
        BBSTNode *temp_node = curr -> left;
        temp_node -> parent = NULL;
        RotateLeft(target);
        Insert(temp_node);
    }
}

右旋代码

cpp 复制代码
void RotateRight(BBSTNode *target){
    BBSTNode *curr = target;
    //分情况判断 有无右子树
    if(curr -> right == NULL){
        curr -> right = target;
        curr -> parent = target -> parent;
        target -> parent = curr;
        if(target -> left == curr) target -> left = NULL;
        else if(target -> right == curr) target -> right = NULL;
    }else{
        BBSTNode *temp_node = curr -> right;
        temp_node -> parent = NULL;
        RotateRight(target);
        Insert(temp_node);
    }
}

代码没运行过,我自己写的,不保证正确性,但是应该是没有问题的

总结

AVL树就是对二叉搜索树的优化,除了需要左旋和右旋,其他地方与二叉搜索树基本没有差别。优化后的二叉搜索树可以保证 O(logn) 的复杂度。反正二叉平衡树是好东西啊好东西,咱得学。

相关推荐
Tisfy2 小时前
LeetCode 0301.删除无效的括号:二进制枚举 / 回溯
算法·leetcode·字符串·题解·回溯·括号匹配·二进制枚举
Nil2082 小时前
leetcode 322零钱兑换
算法·leetcode·职场和发展
海绵宝宝转agent2 小时前
2026-10-9 leetcode100刷题+面经整理
笔记·算法·面试
yi0112 小时前
DAY24: LeetCode 167:两数之和 II|从暴力枚举到左右双指针
笔记·python·算法·leetcode·二分查找·双指针
木井巳2 小时前
【BFS 解决拓扑排序】课程表
java·算法·leetcode·广度优先·宽度优先·推荐算法·图搜索算法
Logic1012 小时前
C语言/数据结构数位DP题解:平滑排列数字统计——区间内相邻位差不超过K的数字个数
c语言·数据结构·动态规划·时间复杂度·数位dp·算法题·前导零处理
扶风ff2 小时前
练题簿在线免费刷题:会员线下组卷、Word 试卷与成绩导入,课堂检测更方便
java·开发语言·算法·小程序·word
程序员老陆3 小时前
C++ 多线程通信方式全景:从共享内存到结构化协调
开发语言·c++
yi0113 小时前
LeetCode 15:三数之和|排序 + 双指针,如何避免重复答案?
笔记·python·算法·leetcode·双指针
-dzk-3 小时前
【多维动态规划】LC 1143.最长公共子序列
算法·动态规划