二叉排序树在使用时可能会出现极端的左或右斜树的现象,导致算法效率大大降低,这称为二叉排序树的不平衡。
而我们在使用的时候,总是希望二叉排序树是平衡的,使算法的效率最大化,所以就有了平衡二叉树(AVL树)。
1、平衡二叉树(AVL树)
从平衡二叉树的英文名字,你也可以体会到,它是一种高度平衡的二叉排序树,那什么叫做高度平衡呢?意思是说,要么它是一棵空树,要么它的左子树和右子树都是平衡二叉树, 并且左子树和右子树的深度之差的绝对值不超过 1。
我们将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子 BF (Balance Factor) ,那么平衡二叉树上所有结点的平衡因子只可能是1、0或-1,只要二叉树上某个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。
很显然之前文章构造的二叉排序树都是不平衡的,左图的二叉排序树的58结点的左子树深度为3,右子树深度为0,就更不用说右图的右斜树了。
我们可以通过适当的调整使左图的二叉排序树变成平衡二叉树,如下图。
以距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,称为最小不平衡子树。
1.1、平衡二叉树(AVL树)的实现原理
平衡二叉树构建的基本思想就是在构建二叉排序树的过程中,每插入一个结点时,先检查是否因插入而破坏树的平衡性,若是,则找出最小不平衡子树。在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。
假设我们需要对数组 a[10]={3, 2, 1, 4, 5, 6, 7, 10, 9, 8}来构建一颗二叉排序树,如果没有学习平衡二叉树,那么根据二叉排序树的性质,我们通常会构建成如下图图1的二叉排序树,虽然它完全符合二叉排序树的定义,但是这样高度达到8的二叉树对于查找、插入和删除的效率来说是非常不利的,而我们更期望能构建成如下图图2的平衡二叉树,高度为4的二叉排序树才可以提供高效的查找、插入和删除效率。
如何构建一颗平衡二叉树呢?
首先给出平衡二叉树的结构,就是一般的二叉树:
typedef int DataType; /*树结点的数据元素类型*/
typedef struct BinaryNode { /*二叉树结点结构*/
DataType data; /*数据域*/
BinaryNode *leftChild; /*左孩子*/
BinaryNode *rightChild; /*右孩子*/
}*BinaryTree;
然后根据平衡因子BF的性质,实现二叉树结点BF值算法:
/*二叉树结点BF值算法*/
int BinaryTreeBF(BinaryTree left, BinaryTree right)
{
int leftChildDepth = BinaryTreeDepth(left); /*得出左子树深度*/
int rightChildDepth = BinaryTreeDepth(right); /*得出右子树深度*/
return (leftChildDepth-rightChildDepth); /*BF=左子树深度-右子树深度*/
}
附录:二叉树深度递归算法
/*二叉树深度递归算法*/
int BinaryTreeDepth(BinaryTree T)
{
if (T == NULL) return 0; /*不存在,所以返回深度为0*/
int leftChildDepth, rightChildDepth, maxDepth;
leftChildDepth = BinaryTreeDepth(T->leftChild); /*递归左子树的深度*/
rightChildDepth = BinaryTreeDepth(T->rightChild); /*递归右子树的深度*/
/*在左子树的深度和右子树的深度中选取最大的,并把这一层的深度加上*/
maxDepth = (leftChildDepth > rightChildDepth) ? (leftChildDepth+1) : (rightChildDepth+1);
return maxDepth;/*返回最大深度*/
}
对于[10]={3, 2, 1, 4, 5, 6, 7, 10, 9, 8}的前两个数3和2,我们正常的构建二叉排序树,但是插入第三个数1时,发现根结点3的BF值为2了,此时整棵树为最小不平衡子树,因此需要调整。
当最小不平衡子树的根结点的BF值为正数时,我们将整个最小不平衡子树进行右旋处理,反之BF值为负数进行左旋处理。
右旋处理后,结点2成为了根结点,结点3成为根结点2的右孩子,此时三个结点的BF值都为0,整颗二叉排序树非常的平衡。
然后我们再插入第四个数4 ,没有结点的BF值超过限定范围。
当我们插入第五个数5时,结点3的BF值为-2,说明需要进行左旋处理,处理后整颗树又达到了平衡。
此时需要解释平衡二叉树的左旋和右旋处理的具体操作: