目录
1.AVL树概念
AVL树:平衡搜索二叉树
二叉搜索树的数据如果接近有序,就会变为单支树,查找效率降低。AVL树应用一种方法——当左右子树高度差超过1,降低树的高度。使二叉搜索树能够一直保持平衡状态。
2.AVL树性质
- 左右子树都是AVL树
- 左右子树高度之差的绝对值不超过1(0/1/-1)
如果AVL树有N个节点。高度=logN,搜索的时间复杂度为O(logN)。
3. 实现
3.1 AVLTreeNode
为了使左右子树高度差不超过1,可以采用很多方法。
在这里通过引入平衡因子来标志左右子树高度差,当平衡因子超过-1或1的时候,降低树的高度。
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; // balance factor平衡因子
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
{}
};
3.2 Insert
平衡因子=右子树高度-左子树高度
成果插入之后,需要更新平衡因子。如果插入到父的左,平衡因子--,如果插入到父的右平衡因子++。然后判断是否需要调整祖先的平衡因子或调整高度。
1.parent->_bf == 0
插入之后,parent左右高度相同了,说明parent树原先就有一个孩子,新增节点刚好是另一个孩子。parent树的高度没变,不需要再往上。
2.parent->_bf == -1 || parent->_bf == 1
父节点的平衡因子原来是0,插入之后++--才会变成1或-1。父这棵树的高度增加了1,祖先的平衡因子也要变,需要往上继续调整。具体往上多少层不能确定,最坏的情况是一直调整到_root。
3.parent->_bf == 2 || parent->_bf == -2
说明parnet之前是1/-1,新增节点后让这颗子树又让这棵树的高度加1。
调整方法为——旋转。
1.让这颗子树树的左右子树高度差不超过1
2.旋转后仍是搜索树
3.更新该子树的平衡因子
4.让这颗子树的高度和之前保持一致,不影响上一层,结束调整平衡因子
1.右边高,新增节点是右孩子(subR)的子树,左单旋
abc是高度为h的子树,紫色是新增节点,高度为1,插入后使子树高度增加1。
左单旋的操作:
1.把subRL放到parent的右,parent放到subR的左。
2.注意链接parent的父(旋转后该子树的根变了)。
3.更新平衡因子。只改变了parent和subR的左右子树,只把这俩的平衡因子改成0。
- h=0
- h=1
- h=2
2. 左边高,新增节点是左孩子的子树,右单旋
3.左边高,新增在左孩子的右子树,左右双旋
新增节点如果使d或e的高度增加1,就会向上调节平衡因子,导致parent变成-2。
1)h为0
2)h不为0
- 新增在d之下
- 新增在e之下
单旋中直接控制平衡因子为0的方法已经不适用了,这里新增在d下和e下,平衡因子不同。
可以通过subLR在插入后的平衡因子,推断出在d下还是e下。
4.右边高,新增在右孩子的左子树,右左双旋
1)h为0
2)h不为0
- 新增在d之下
- 新增在e只下
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
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
{
return false;
}
}
cur = new Node(kv);
//cur为空时,应该插入新节点。new了之后并不表示和树链接上了,cur只是父节点左或右子树地址的拷贝,修改它(把new出的新节点给cur)对父节点没有一点影响。还需要记录父节点。不清楚cur到底是左还是右,再判断一次。
if (parent->_kv.first > kv.first)
{
parent->_left = cur;
cur->_parent = parent;
}
else
{
parent->_right = cur;
cur->_parent = parent;
}
//成功插入之后要更新平衡因子
while (parent)
{
if (cur == parent->_left)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
//判断需不需要再向上去调整parent的parent
if (parent->_bf == 0)//插入之后,parent左右高度相同了,说明parent树原先就有一个孩子,新增节点刚好是另一个孩子。parent树的高度没变,不需要再往上。
{
break;
}
else if (parent->_bf == -1 || parent->_bf == 1)//父节点的平衡因子原来是0,插入之后++--才会变成1或-1。父这棵树的高度增加了1,祖先的平衡因子也要变,需要往上继续调整。
{
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)//双旋,先右后左
{
RotateRL(parent);
}
else if (parent->_bf == -2 || cur->_bf == 1)//双旋,先左后右
{
RotateLR(parent);
}
else
{
assert(false);
}
break;//旋转结束后,高度保持不变,对父没有影响,不需要再往上去调整
}
else
{
assert(false);
}
}
return true;
}
//任务1.把subRL放到parent的右,parent放到subR的左。
//2.注意链接_parent。注意链接parent的父(旋转后该子树的根变了)
//3.更新平衡因子。只改变了parent和subR的左右子树,只把这俩的平衡因子改成0。
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)//当h为0的时候,subRL为空
{
subRL->_parent = parent;
}
Node* ppNode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppNode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subR;
}
else
{
ppNode->_right = subR;
}
subR->_parent = ppNode;
}
parent->_bf = subR->_bf = 0;
}
void RotateR(Node* parent)//左边高
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
//parent去subL的右
Node* ppNode = parent->_parent;
parent->_parent = subL;
subL->_right = parent;
//ppNode和subL
if (ppNode)
{
if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
subL->_parent = ppNode;
}
else
{
_root = subL;
_root->_parent = nullptr;
}
parent->_bf = subL->_bf = 0;
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
if (bf == -1)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf == 0)//subLR自己就是新增
{
parent->_bf = 0;
subL->_bf = 0;
subLR->_bf=0;
}
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 == 1)
{
subRL->_bf = 0;
parent->_bf = -1;
subR->_bf = 0;
}
else if (bf == -1)
{
subRL->_bf = 0;
parent->_bf = 0;
subR->_bf = 1;
}
else if (bf == 0)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
private:
Node* _root=nullptr;
};
3.3 Inorder
void Inorder()
{
_Inorder(_root);
}
void _Inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_Inorder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_Inorder(root->_right);
}
3.4 Height
int Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int lh = Height(root->_left);
int rh = Height(root->_right);
return lh > rh ? lh + 1 : rh + 1;
}
3.5 IsBalance
bool IsBalance()
{
return _IsBalance(_root);
}
bool _IsBalance(Node* root)
{
if (root == nullptr)
{
return true;
}
int leftHeight = Height(root->_left);
int rightHeight = Height(root->_right);
if (rightHeight - leftHeight != root->_bf)
{
cout << root->_kv.first << "平衡因子异常" << endl;
return false;
}
return abs(rightHeight - leftHeight) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}