搜索二叉树能够在二叉树情况比较好的情况下,使查找的时间复杂度达到O(logN)。
但是,它的查找的时间复杂度依旧是O(N),面临的情况是所有的树都只有左/右子树的情况下。
那么今天介绍的AVL树就是解决这一情况的。
但是由于AVL树对我来说有些复杂,所以只讨论插入节点。这其实也有了查和改,没有删除。
AVL树是在搜索树的基础上对搜索树提出了额外的规则和调整的方法的一种树,它提出了 平衡因子的概念,利用平衡因子来体现搜索树是否平衡,而当平衡因子是非常规值的时候就会通过旋转树来让平衡因子重新达到平衡。
而平衡因子就是一棵树的右子树的高度减去左子树的高度,这个值的绝对值不大于 1,则认为这棵树是平衡的举个例子:
而这棵树不符合上述我们对平衡二叉树的要求,所以不是平衡二叉树,得需要旋转调整:
现在,它就是一颗平衡二叉树了。所以接下来来让我们实现这一数据结构:
与二叉搜索树不同,平衡二叉树中多了两个成员,一个是平衡因子,一个是指向父亲节点的指针:
而这里我少做了些调整,将存储数据换为pair结构体的Key-Value形。
template<class Key, class Val>
struct AVLTreeNode
{
AVLTreeNode<Key, Val>* _left;
AVLTreeNode<Key, Val>* _right;
AVLTreeNode<Key, Val>* _parent;
pair<Key, Val> _kv;
int _bf;//balance factor
AVLTreeNode(const pair<Key, Val>& pa)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(pa)
,_bf(0)
{}
};
AVL树前面说过,它是在二叉搜索树的基础上增加一些规则形成的,所以前半部分与二叉搜索树的插入一样:
bool Insert(const pair<Key, Val>& pa)
{
if (_root == nullptr)
{
_root = new Node(pa);
size++;
return true;
}
else
{
//正常按搜索树规则插入节点
Node* cur = _root;
Node* parent = _root;
while (cur)
{
parent = cur;
if (cur->_kv.first > pa.first)
cur = cur->_left;
else if (cur->_kv.first < pa.first)
cur = cur->_right;
else
return false;
}
cur = new Node(pa);
if (parent->_kv.first < cur->_kv.first)
parent->_right = cur;
else if(parent->_kv.first > cur->_kv.first)
parent->_left = cur;
//更新插入节点的父亲节点
cur->_parent = parent;
//开始遵守平衡二叉树的规则。。。
return true;
}
}
当插入节点后,再开始更新平衡因子,然后根据平衡因子进行调整。我们可以从中找出一些规律后开始书写代码:以这棵树为例:
我们先插入一个数字为33的节点会得到这样的情况:
我们插入一个数字为55的节点会得到这样的情况:
我们发现我们插入的新节点的平衡因子总是0,而且新增节点只会影响祖先节点,不会影响旁支。
我们还发现,当向上更新祖先平衡因子时并不是只更新父亲,也不是一直更新到根节点,那这有没有规律呢?其实是有的:
当父亲节点的平衡因子更新后变成0的时候,说明它是从-1或者1变来的,也说明了它增加这个节点之后
并没有增加父亲这颗子树的高度,所以更新了父亲节点的平衡因子就不需要向上更新了。
而当父亲节点的平衡因子更新后变成-1/1的时候,说明原来父亲节点的平衡因子是0,处于一种平衡的状态,
而后又打破了平衡,增加了父亲子树的高度,所以需要更新父亲节点的平衡因子后继续向上更新。
而且新增节点是父亲节点的左子树的时候父亲的平衡因子++,右子树时--
依据上述我们可以写出部分代码:
while (parent)
{
//调整平衡因子
//如果插入节点在父亲节点的左边,减减
//右边加加
if (parent->_left == cur) parent->_bf--;
else if(parent->_right == cur) parent->_bf++;
//如果调整平衡因子后父亲节点的平衡因子为0,说明以前的高度是1或者-1,高度没发生变化不用向上调整
//如果为1或者-1需要向上调整平衡因子
if (parent->_bf == 0) break;
else if (parent->_bf == -1 || parent->_bf == 1) parent = parent->_parent, cur = cur->_parent;
else
{
}
}
这是我们碰到插入节点后平衡因子没有出现违反规则的情况,那假如我们插入一个65的节点呢?
我们发现它的平衡因子就不符合规则了,那这种情况下,就需要旋转来调整。
由于二叉树所种多样,不可能将所有的情况都一一列举,所以我们使用一种统一的视角来看待AVL树的旋转:
我们在上面碰到的两种不平衡的情况都是需要左单旋来解决:
最后一个树中,a是一颗高度为h的子树,b、c是高度为h-1的子树,然后在c子树中增加新节点。导致根节点(暂且是根节点)的平衡因子变成了2。需要旋转:
在这里我们对一些节点起一些名字:
这时候需要我们对parent进行一次左旋,旋转的具体做法就是将subR的左子树subLR给parent,
变成parent的右子树,然后把parent变为subR的左子树:
此时我们可以看到,parent的平衡因子变成了0,subR的变成了0。
接下来代码实现:
void RotateL(Node* parent)
{
Node* subRL = parent->_right->_left;
Node* subR = parent->_right;
Node* graparent = parent->_parent;
parent->_right = subRL;
parent->_parent = subR;
if(subRL)//这里处理c就是新增的情况,防止subL为空
subRL->_parent = parent;
subR->_left = parent;
subR->_parent = graparent;
if (graparent)
{
if (graparent->_left == parent) graparent->_left = subR;
else graparent->_right = subR;
}
else
_root = subR;
parent->_bf = subR->_bf = 0;
}
而左单旋的特征就是:parent的平衡因子是2,subR为1
为什么不一一列举,假如上面的h-1等于2的话,说明abc都是一个高度为2的子树那么首先高度为2的子树就有3种情况,而在c树上新增一个节点可能有8种情况,组合一下就是216种情况,列举不完…
右单旋和左单旋对称,不多说,代码如下:
void RotateR(Node* parent)
{
Node* subLR = parent->_left->_right;
Node* subL = parent->_left;
Node* graparent = parent->_parent;
parent->_left = subLR;
parent->_parent = subL;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
subL->_parent = graparent;
if (graparent)
{
if (graparent->_left == parent) graparent->_left = subL;
else graparent->_right = subL;
}
else
_root = subL;
parent->_bf = subL->_bf = 0;
}
而右单旋的特征就是:parent的平衡因子是-2,subR为-1
我们插入节点的时候可能会有这种情况,例如我们插入55:
这次我们再用一次左单旋:
没有什么变化,所以我们得使用双旋:
这时候我们需要再使用统一的图来说明:
再起名字:
这时候双旋需要我们将subR右旋:
如果有细心的人发现,这其实就是把b子树做了parent的右子树,c子树做了subR,然后subRL做了parent和subR的父亲,这其中新增节点的位置影响着parent和subR的平衡因子,调整方案如下:
当新增节点在b上,也就是subRL的平衡因子为-1时,parent的平衡因子为0,subRL的为0, subR的为1
当新增节点在c上,也就是subRL的平衡因子为1时,parent的平衡因子为-1,subRL的为0, subR的为0
当然还有subRL本身就是新增的情况,subRL的平衡因子为0,此时parent、subRsubRL的平衡因子都是0
代码如下:
void RotateRL(Node* parent)
{
int subRL_bf = parent->_right->_left->_bf;
Node* subR = parent->_right;
Node* subRL = parent->_right->_left;
RotateR(parent->_right);
RotateL(parent);
if (subRL_bf == 0)
parent->_bf = subR->_bf = subRL->_bf = 0;
else if (subRL_bf == 1)
{
subRL->_bf = subR->_bf = 0;
parent->_bf = -1;
}
else if (subRL_bf == -1)
{
parent->_bf = subRL->_bf = 0;
subR->_bf = 1;
}
}
void RotateLR(Node* parent)
{
int subLR_bf = parent->_left->_right->_bf;
Node* subL = parent->_left;
Node* subLR = parent->_left->_right;
RotateL(parent->_left);
RotateR(parent);
if (subLR_bf == 0)
parent->_bf = subL->_bf = subLR->_bf = 0;
else if (subLR_bf == 1)
{
subLR->_bf = parent->_bf = 0;
subL->_bf = -1;
}
else if (subLR_bf == -1)
{
subL->_bf = subLR->_bf = 0;
parent->_bf = 1;
}
}
上面就是AVL树的最重要的点,我们在创建好一颗平衡二叉树的时候也应该检查,它是否真的平衡。而平衡二叉树的的规则就是高度的限制,所以我们只需要看每棵树的左右子树的高度差即可,代码如下:
bool IsBlance()
{
return _IsBlance(_root);
}
int Height(Node* root)
{
if (root == nullptr)
return 0;
int left = Height(root->_left);
int right = Height(root->_right);
return left > right ? 1 + Height(root->_left) : 1 + Height(root->_right);
}
int _IsBlance(Node* root)
{
if (root == nullptr)
return true;
int left = Height(root->_left);
int right = Height(root->_right);
return abs(right - left) > 1 ? false : _IsBlance(root->_left)
&& _IsBlance(root->_right);
}
最后附上整合好的AVL树:
template<class Key, class Val>
struct AVLTreeNode
{
AVLTreeNode<Key, Val>* _left;
AVLTreeNode<Key, Val>* _right;
AVLTreeNode<Key, Val>* _parent;
pair<Key, Val> _kv;
int _bf;
AVLTreeNode(const pair<Key, Val>& pa)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(pa)
,_bf(0)
{}
};
template<class Key, class Val>
class AVLTRree
{
typedef AVLTreeNode<Key, Val> Node;
public:
int size = 0;
bool Insert(const pair<Key, Val>& pa)
{
if (_root == nullptr)
{
_root = new Node(pa);
size++;
return true;
}
else
{
Node* cur = _root;
Node* parent = _root;
while (cur)
{
parent = cur;
if (cur->_kv.first > pa.first)
cur = cur->_left;
else if (cur->_kv.first < pa.first)
cur = cur->_right;
else
return false;
}
cur = new Node(pa);
if (parent->_kv.first < cur->_kv.first)
parent->_right = cur;
else if(parent->_kv.first > cur->_kv.first)
parent->_left = cur;
cur->_parent = parent;
while (parent)
{
if (parent->_left == cur) parent->_bf--;
else if(parent->_right == cur) parent->_bf++;
if (parent->_bf == 0) break;
else if (parent->_bf == -1 || parent->_bf == 1) parent = parent->_parent, cur = cur->_parent;
else if (parent->_bf == 2 || parent->_bf == -2)
{
if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else assert(false);
}
else assert(false);
}
size++;
return true;
}
}
bool IsBlance()
{
return _IsBlance(_root);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
//cout << root->_kv.first << " ";
printf("%2d ", root->_kv.first);
_InOrder(root->_right);
}
int Height(Node* root)
{
if (root == nullptr)
return 0;
int left = Height(root->_left);
int right = Height(root->_right);
return left > right ? 1 + Height(root->_left) : 1 + Height(root->_right);
}
int _IsBlance(Node* root)
{
if (root == nullptr)
return true;
int left = Height(root->_left);
int right = Height(root->_right);
return abs(right - left) > 1 ? false : _IsBlance(root->_left)
&& _IsBlance(root->_right);
}
void RotateLR(Node* parent)
{
int subLR_bf = parent->_left->_right->_bf;
Node* subL = parent->_left;
Node* subLR = parent->_left->_right;
RotateL(parent->_left);
RotateR(parent);
if (subLR_bf == 0)
parent->_bf = subL->_bf = subLR->_bf = 0;
else if (subLR_bf == 1)
{
subLR->_bf = parent->_bf = 0;
subL->_bf = -1;
}
else if (subLR_bf == -1)
{
subL->_bf = subLR->_bf = 0;
parent->_bf = 1;
}
}
void RotateRL(Node* parent)
{
int subRL_bf = parent->_right->_left->_bf;
Node* subR = parent->_right;
Node* subRL = parent->_right->_left;
RotateR(parent->_right);
RotateL(parent);
if (subRL_bf == 0)
parent->_bf = subR->_bf = subRL->_bf = 0;
else if (subRL_bf == 1)
{
subRL->_bf = subR->_bf = 0;
parent->_bf = -1;
}
else if (subRL_bf == -1)
{
parent->_bf = subRL->_bf = 0;
subR->_bf = 1;
}
}
void RotateL(Node* parent)
{
Node* subRL = parent->_right->_left;
Node* subR = parent->_right;
Node* graparent = parent->_parent;
parent->_right = subRL;
parent->_parent = subR;
if(subRL)
subRL->_parent = parent;
subR->_left = parent;
subR->_parent = graparent;
if (graparent)
{
if (graparent->_left == parent) graparent->_left = subR;
else graparent->_right = subR;
}
else
_root = subR;
parent->_bf = subR->_bf = 0;
}
void RotateR(Node* parent)
{
Node* subLR = parent->_left->_right;
Node* subL = parent->_left;
Node* graparent = parent->_parent;
parent->_left = subLR;
parent->_parent = subL;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
subL->_parent = graparent;
if (graparent)
{
if (graparent->_left == parent) graparent->_left = subL;
else graparent->_right = subL;
}
else
_root = subL;
parent->_bf = subL->_bf = 0;
}
Node* _root = nullptr;
};