1.1 AVL树的概念
template<class K,class V>
struct AVLTreeNode
{
pair<K, V> _kv;//存储的数值
AVLTreeNode<K,V>* left;
AVLTreeNode<K, V>* right;
AVLTreeNode<K, V>* parent;//此节点的父亲结点
int _bf;//平衡因子
};
当然我们也可以不把数值存成pair的形式,这样是和map一致,各有各的用处
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv)
,left(nullptr)
,right(nullptr)
,parent(nullptr)
,_bf(0)
{
}
我们用初始化列表的方式完成构造函数,也不算完成吧,因为这个是需要参数的构造函数,这个应该叫做赋值拷贝。
1.3 AVL树的插入
主要分为两步:1. 按照二叉搜索树的方式插入新节点 2. 调整节点的平衡因子
看着简单实则不太容易。
![](https://i-blog.csdnimg.cn/direct/fe0f0f01c2e34366888bb92ff95574cd.png)
(此截图来着bili的up主:蓝不过海呀)
主要就是用来失衡怎么做
首先是右单旋,也就是出现此情况的时候,我们要进行右旋,
,也就是形成该样子,把平衡因子为2的14结点向下转,也就是右旋,转成六结点的右孩子,然后6的右孩子,就成了14的左孩子,6的右孩子是空也无妨。
void RotateR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
parent->left = subLR;
if (subLR)
subLR->parent = parent;
Node* PParent = parent->parent;
subL->right = parent;
parent->parent = subL;
if (PParent == nullptr)
{
root = subL;
}
else
{
if (PParent->right == parent)
PParent->right = subL;
else
PParent->left = subL;
}
subL->parent = PParent;
subL->_bf = parent->_bf = 0;
}
我们把失衡的结点也就是图中的14,传过来,传给parent。由于我们刚才分析过了,会改变6和6的右孩子,所以我们记录此时这几个结点,然后进行改变,最后6顶替了14的位置,如果原来14有父亲的话,要让6成为原来14父亲的孩子,进行重新链接
左旋就是同样的道理
void RotateL(Node* parent)
{
Node* subR = parent->right;
Node* subRL = subR->left;
parent->right = subRL;
if (subRL)
subRL->parent = parent;
Node* PParent = parent->parent;
subR->left = parent;
parent->parent = subR;
if (PParent)
{
subR->parent = PParent;
if (PParent->left == parent)
PParent->left = subR;
else
PParent->right = subR;
}
else
{
subR->parent = nullptr;
root = subR;
}
//平衡因子
subR->_bf = parent->_bf = 0;
}
我们再代码区域看到了,我已经进行了平衡因子的更新,接下来我们说一下单旋的平衡因子是怎么更新的。我们以左单旋为例子。
,不要纠结为什么是h啥的高度,只是为了更好的计算平衡因子,相当于数学的代入字母证明的一般式
,所以我们可以得到,如果单选的话,parent和parent->left或者right 的平衡因子都更新成0。
接着我们来解决另外两种双旋的,举例子我们用先左旋再右旋的,也就是LR型
先进行第一次旋转,这是一次左旋
,接上的话,
,就又成为了应该刚才右旋的,因为此时的平衡因子是-2 和-2.总结一下,就是失衡的结点再次成为parent ,然后parent的left要往下转,也就是左旋,然后成为它右孩子的子结点,也就是成为parent->left->right的子结点,正好对应了LR型,然后进行右旋,
使得parent->left->right成为新的此时的“根”结点。
void RotateLR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
int bf = subLR->_bf;
RotateL(parent->left);
RotateR(parent);
if (bf == 0)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 1;
}
else
assert(false);
}
void RotateRL(Node* parent)
{
Node* subR = parent->right;
Node* subRL = subR->left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(parent);
if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subRL->_bf = 0;
subR->_bf = 1;
parent->_bf = 0;
}
else
assert(false);
}
我们可以看到,我们的旋转用的之前写好的左单旋和右单旋,然后我们只需要更新平衡因子就行了。其实双旋的平衡因子才是麻烦事我们再次用一般形式来进行计算。最后我们做出总结:只要看 第三个结点,也就是parent->left->right 或者事 parent->right->left 的平衡因子分为三种情况,大家可以多画画图就知道代码写的意思了。
两种种就是以上两张图片的情况,
第一种是为平衡因子为1的情况,
第二种是为-1的。都是分别进行左右双旋。
最后一种就是是插入之后,它的平衡因子为零,那么改动的三个结点:parent parent->left parent->left->right(parent parent->right parnet->right->left) 的平衡因子都要成为0;
以上霓虹颜色的截图都来自于up主蓝不过海呀 的视频。
最后分享完整代码
bool insert(const pair<K, V>& kv)
{
if (root == nullptr)
{
root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = root;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->left;
}
else if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->right;
}
else
{
cout << "已经重复了" << endl;
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first > cur->_kv.first)
{
parent->left = cur;
}
else
{
parent->right = cur;
}
cur->parent = parent;
//更新平衡因子
while (parent)
{
if (cur == parent->left)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == -1 || parent->_bf == 1)
{
//祖先需要更新,所以父亲和cur网上挪,然后再次循环就会到2的条件
cur = parent;
parent = parent->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)
{
RotateLR(parent);
}
else
{
RotateRL(parent);
}
break;
}
else
{
assert(false);
}
}
return true;
}
void RotateL(Node* parent)
{
Node* subR = parent->right;
Node* subRL = subR->left;
parent->right = subRL;
if (subRL)
subRL->parent = parent;
Node* PParent = parent->parent;
subR->left = parent;
parent->parent = subR;
if (PParent)
{
subR->parent = PParent;
if (PParent->left == parent)
PParent->left = subR;
else
PParent->right = subR;
}
else
{
subR->parent = nullptr;
root = subR;
}
//平衡因子
subR->_bf = parent->_bf = 0;
}
void RotateR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
parent->left = subLR;
if (subLR)
subLR->parent = parent;
Node* PParent = parent->parent;
subL->right = parent;
parent->parent = subL;
if (PParent == nullptr)
{
root = subL;
}
else
{
if (PParent->right == parent)
PParent->right = subL;
else
PParent->left = subL;
}
subL->parent = PParent;
subL->_bf = parent->_bf = 0;
}
void RotateLR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
int bf = subLR->_bf;
RotateL(parent->left);
RotateR(parent);
if (bf == 0)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 1;
}
else
assert(false);
}
void RotateRL(Node* parent)
{
Node* subR = parent->right;
Node* subRL = subR->left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(parent);
if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subRL->_bf = 0;
subR->_bf = 1;
parent->_bf = 0;
}
else
assert(false);
}