AVL树的定义及其特性:
平衡二叉树(Balanced Binary Tree 或 Height-Balanced Tree)又称AVL树。它或者是一颗空树,或者是具有以下性质的二叉树:
- 它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1;
- 该树上任意结点的平衡因子BF(Balance Factor)定义为该结点左子树的深度减去右子树的深度,则平衡因子只能取-1、0、1,否则表示该树不平衡。
由上面所提出的性质可知,AVL树实际上只是BST树的延伸。相对与BST树,它对于树的结构提出了更加苛刻的要求。分析容易得知,在添加结点和删除结点的过程中,可能会导致树失去平衡性质,因此还需在删除结点之后重新对树进行排序,保证树的平衡性质。
结构定义:
根据以上描述,AVL树不过是在BST树上根据平衡因子判断进行了一些限制与调整,其树的基本结构并没有改变,依旧是采用二叉链表的方式进行了结构定义。但与BST树不同的是,AVL树需要为每个结点增加一个成员变量height,表示该结点的高度。为了判断增加或删除某个结点是否会导致整颗树的失衡,还需定义两个API接口,分别用来返回某个结点的高度以及该结点左右子树的高度差。
具体实现:
class AVLTree
{
public:
AVLTree() :_root(nullptr)
{}
void insert(const int val);
void erese(const int val);
void levelOrder();
private:
struct AVLNode
{
AVLNode(int data = 10)
:_data(data)
,_left(nullptr)
,_right(nullptr)
,_height(1) //单独一个结点自身看来其深度为1
{}
int _data;
AVLNode* _left;
AVLNode* _right;
int _height; //表示当前结点的高度
};
AVLNode* _root;
AVLNode* insert(AVLNode* node, const int val);
AVLNode* erease(AVLNode* node, const int val);
int height(AVLNode* node); //返回结点的高度
int maxHeight(AVLNode* node1, AVLNode* node2);
AVLNode* LeftRotate(AVLNode* node); //左旋操作
AVLNode* RightRotate(AVLNode* node); //右旋操作
AVLNode* LeftBalance(AVLNode* node); //左右旋转
AVLNode* RightBalance(AVLNode* node); //右左旋转
int AVLTreeLenth(AVLNode* node);
void levelOrder(AVLNode* node,int lenth)const;
};
int AVLTree::height(AVLNode* node)
{
return node == nullptr ? 0 : node->_height;
}
int AVLTree::maxHeight(AVLNode* node1, AVLNode* node2)
{
return height(node1) > height(node2) ? height(node1) : height(node2);
}
关于AVL树因添加结点导致失衡的原因为以下四种情况:
1、在某个结点的左子树中插入一个左孩子导致失衡(LL),采取右旋转策略;
2、在某个结点的右子树中插入一个右孩子导致失衡(RR),采取左旋转策略;
3、在某个结点的左子树中插入一个右孩子导致失衡(LR),采取先左旋后右旋策略;
4、在某个结点的右子树中插入一个左孩子导致失衡(RL),采取先右旋后左旋策略;
以下分别进行介绍:
右旋转:
由于向某节点的左子树中插入一个左孩子导致该结点失衡,则需要使用右旋转来维护AVL树的平衡;
具体情境为:
代码实现:
AVLTree::AVLNode* AVLTree::RightRotate(AVLNode* node)
{
AVLNode* child = node->_left;
node->_left = child->_right;
child->_right = node;
node->_height = maxHeight(node->_left, node->_right) + 1;
child->_height = maxHeight(node->_left, node->_right) + 1;
return child;
}
左旋转:
由于向某节点的右子树中插入一个右孩子导致该结点失衡,则需要使用左旋转来维护AVL树的平衡;
具体情境为:
代码实现:
AVLTree::AVLNode* AVLTree::RightRotate(AVLNode* node)
{
AVLNode* child = node->_right;
node->_right = child->_left;
child->_left = node;
node->_height = maxHeight(node->_left,node->_right) + 1;
child->_height = maxHeight(child->_left, child->_right) + 1;
return child;
}
左右旋转:
由于向某节点的左子树中插入一个右孩子导致该结点失衡,则需要使用左右旋转来维护AVL树的平衡;
具体情境为:
代码实现:
AVLTree::AVLNode* AVLTree::LeftBalance(AVLNode* node)
{
node->_left = LeftRotate(node->_left);
return RightRotate(node);
}
右左旋转:
由于向某结点的右子树中插入一个左孩子导致该结点失衡,则需要进行右左旋转来维护AVL树的平衡;
具体情境为:
代码实现:
AVLTree::AVLNode* AVLTree::RightBalance(AVLNode* node)
{
node->_right = RightRotate(node->_right);
return LeftRotate(node);
}
在了解了以上知识后,下面来具体说明AVL树的插入以及删除操作:
AVL的插入:
AVL树的插入与二叉排序树(BST)树的插入方法基本类似。其区别在于插入完当前结点时,需判断该结点父节点左右子树的高度情况。如果左右子树的高度差不满足平衡因子,则说明该结点的插入导致了整棵树的失衡。紧接着需要判断具体是什么原因呢导致的失衡,然后根据递归回溯的特点对整棵树进行调整。
具体代码实现:
AVLTree::AVLNode* AVLTree::insert(AVLNode* node, const int val)
{
if (node == nullptr)
return new AVLNode(val);
if (node->_data > val)
{
node->_left = insert(node->_left, val);
if (height(node->_left) - height(node->_right) > 1)
{
//说明左子树失衡,此时需要判断是因为左子树左孩子导致的失衡,还是左子树右孩子导致的失衡
if (height(node->_left->_left) >= height(node->_left->_right))
{
//左子树太高,属于LL情况
node = RightRotate(node);
}
else
{
//右子树太高,属于LR情况
node = LeftBalance(node);
}
}
}
else if (node->_data < val)
{
node->_right = insert(node->_right, val);
if (height(node->_right) - height(node->_left) > 1)
{
if (height(node->_right->_right) >= height(node->_right->_left))
{
// 右孩子的右子树太高,属于RR情况
node = LeftRotate(node);
}
else
{
// 右孩子的左子树太高,属于RL情况
node = RightBalance(node);
}
}
}
else
{
;
}
// 在递归回溯过程中,更新节点的高度值
node->_height = maxHeight(node->_left, node->_right) + 1;
return node;
}
AVL的删除:
AVL树结点的删除同样和二叉排序树(BST)基本类似。区别在于删除完一个结点后,需判断是否会引起整棵树的失衡,如果发生失衡,则需要做出相应调整。具体这里不在详细赘述。关于二叉排序树(BST)的相关知识可以参考我的其他博文。
具体实现:
void AVLTree::erese(const int val)
{
this->_root = erease(_root, val);
}
AVLTree::AVLNode* AVLTree::erease(AVLNode* node, const int val)
{
if (node == nullptr)
return nullptr;
if (node->_data > val)
{
node->_left = erease(node->_left, val);
if (height(node->_right) - height(node->_left) > 1)
{
//说明右子树高
if (height(node->_right->_right) >= height(node->_right->_left))
{
//属于RR
node = LeftRotate(node);
}
else
{
//属于RL
node = RightBalance(node);
}
}
}
else if (node->_data < val)
{
node->_right = erease(node->_right, val);
if (height(node->_left) - height(node->_right) > 1)
{
//说明左子树高
if (height(node->_left->_left) >= height(node->_left->_right))
{
//属于LL
node = RightRotate(node);
}
else
{
//属于LR
node = LeftBalance(node);
}
}
}
else
{
//找到待删除结点的位置,判断情况二
if (node->_left != nullptr && node->_right != nullptr)
{
// 前驱替换
AVLNode* pre = node->_left;
while (pre->_right != nullptr)
{
pre = pre->_right;
}
//将node的值改为前驱结点的值
node->_data = pre->_data;
//直接删除前驱结点,前驱结点一定在当前结点的左子树中
node->_left = erease(node->_left, pre->_data);
if (height(node->_right) - height(node->_left) > 1)
{
//说明右子树高
if (height(node->_right->_right) >= height(node->_right->_left))
{
//属于RR
node = LeftRotate(node);
}
else
{
//属于RL
node = RightBalance(node);
}
}
}
//情况一,只有一个左子树
else
{
if (node->_left != nullptr)
{
AVLNode* pcur = node->_left;
delete node;
return pcur;
}
//情况二,只有一个右子树
else if (node->_right != nullptr)
{
AVLNode* pcur = node->_right;
delete node;
return pcur;
}
//情况二,为叶子结点
{
return nullptr;
}
}
}
node->_height = maxHeight(node->_left, node->_right) + 1;
return node;
}
关于AVL树的完整代码,参考以下链接:(其中还有RB树,字典树等数据结构具体实现,感兴趣的可以访问下哦!https://github.com/fdklcl/data-structure-method-achieve)