AVL树简介
AVL树的名字来源于它的发明作者G.M. Adelson-Velsky 和 E.M. Landis。AVL树是最先发明的自平衡二叉查找树(Self-Balancing Binary Search Tree,简称平衡二叉树)。
一棵AVL树有如下必要条件:
- 条件一:它必须是二叉查找树。
- 条件二:每个节点的左子树和右子树的高度差至多为1。
图一中左边二叉树的节点45的左孩子46比45大,不满足二叉搜索树的条件,因此它也不是一棵平衡二叉树。
右边二叉树满足二叉搜索树的条件,同时它满足条件二,因此它是一棵平衡二叉树。
左边二叉树的节点45左子树高度2,右子树高度0,左右子树高度差为2-0=2,不满足条件二;
右边二叉树的节点均满足左右子树高度差至多为1,同时它满足二叉搜索树的要求,因此它是一棵平衡二叉树。
AVL树的查找、插入、删除操作在平均和最坏的情况下都是O(logn),这得益于它时刻维护着二叉树的平衡。如果我们需要查找的集合本身没有顺序,在频繁查找的同时也经常的插入和删除,AVL树是不错的选择。不平衡的二叉查找树在查找时的效率是很低的,因此,AVL如何维护二叉树的平衡是我们的学习重点。
AVL树相关概念
平衡因子:将二叉树上节点的左子树高度减去右子树高度的值称为该节点的平衡因子BF(Balance Factor)。
在图二右边的AVL树上:
节点50的左子树高度为3,右子树高度为2,BF= 3-2 = 1;
节点45的左子树高度为2,右子树高度为1,BF= 2-1 = 1;
节点46的左子树高度为0,右子树高度为0,BF= 0-0 = 0;
节点65的左子树高度为0,右子树高度为1,BF= 0-1 = -1;
对于平衡二叉树,BF的取值范围为[-1,1]。如果发现某个节点的BF值不在此范围,则需要对树进行调整。最小不平衡子树:距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树。
在图三中,左边二叉树的节点45的BF = 1,插入节点43后,节点45的BF = 2。节点45是距离插入点43最近的BF不在[-1,1]范围内的节点,因此以节点45为根的子树为最小不平衡子树。
AVL树的实现详解
1. 节点结构
struct AVLTreeNode
{
AVLTreeNode(T value, AVLTreeNode<T>*l, AVLTreeNode<T>*r)
:key(value), lchild(l), rchild(r){}
T key;
int height;
AVLTreeNode<T>* lchild;
AVLTreeNode<T>* rchild;
};
AVL的节点结构为AVLTreeNode,它包括:
- key:节点的值
- height: 节点的高度,用于计算父节点的平衡因子
- lchild : 若节点有左子树,则lchild指向节点的左孩子,否则指向nullptr
- rchild : 若节点有右子树,则rchild指向节点的右孩子,否则指向nullptr
在另外一些AVL实现的节点设计方案中,会把BF作为结点的一个属性存储起来,而在这里我们存储的是节点的高度,通过节点的高度我们也可以间接计算出节点的BF。例如节点A的左孩子的height = 2,右孩子的height = 1,那么节点A的平衡因子为2 - 1 = 1.
2. AVL树的抽象数据结构(ADT)
template<typename T>
class AVLTree
{
public:
AVLTree(); //构造函数
~AVLTree(); //析构函数
void preOrder(); //前序遍历AVL树
void InOrder(); //中序遍历AVL树
void postOrder(); //后序遍历AVL树
void print(); //打印AVL树
void destory(); //销毁AVL树
void insert(T key); //插入指定值的节点
void remove(T key); //移除指定值的节点
AVLTreeNode<T>* search_recurse(T key); //利用递归算法进行指定值的查找
AVLTreeNode<T>* search_iterator(T key); //利用迭代算法进行指定值的查找
T minimum(); //返回AVL中的最小值
T maximum(); //返回AVL中的最大值
int height(); //返回树的高度
private:
AVLTreeNode<T>* root; //AVL树的根节点
private:
void preOrder(AVLTreeNode<T>* pnode) const;
void inOrder(AVLTreeNode<T>* pnode) const;
void postOrder(AVLTreeNode<T>* pnode) const;
void print(AVLTreeNode<T>* pnode,T key, int direction) const;
void destory(AVLTreeNode<T>* & pnode);
AVLTreeNode<T>* insert(AVLTreeNode<T>* &pnode, T key);
AVLTreeNode<T>* remove(AVLTreeNode<T>* & pnode, AVLTreeNode<T>* pdel); //删除AVL树中节点pdel,并返回被删除的节点
AVLTreeNode<T>* minimum(AVLTreeNode<T>*pnode)const;
AVLTreeNode<T>* maximum(AVLTreeNode<T>*pnode)const;
AVLTreeNode<T>* search_recurse(AVLTreeNode<T>* pnode, T key) const;
AVLTreeNode<T>* search_iterator(AVLTreeNode<T>* pnode, T key) const;
AVLTreeNode<T>* leftRotation(AVLTreeNode<T>* pnode); //单旋:左旋操作
AVLTreeNode<T>* rightRotation(AVLTreeNode<T>* pnode); //单旋:右旋操作
AVLTreeNode<T>* leftRightRotation(AVLTreeNode<T>* pnode); //双旋:先左旋后右旋操作
AVLTreeNode<T>* rightLeftRotation(AVLTreeNode<T>* pnode); //双旋:先右旋后左旋操作
};