AVL树
前言
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
一、AVL是什么?
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
二、满足条件
1.它的左右子树都是AVL树
2.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
三、实现AVL树
#pragma once
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode
<K
, V
>* _left
;
AVLTreeNode
<K
, V
>* _right
;
AVLTreeNode
<K
, V
>* _parent
;
int _bf
;
pair
<K
, V
> _kv
;
AVLTreeNode(const pair
<K
, V
>& kv
)
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv
)
, _bf(0)
{}
};
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
);
if (parent
->_kv
.first
< kv
.first
)
{
parent
->_right
= cur
;
cur
->_parent
= parent
;
}
else
{
parent
->_left
= 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
= parent
;
parent
= parent
->_parent
;
}
else if (parent
->_bf
== 2 || parent
->_bf
== -2)
{
if (parent
->_bf
== 2)
{
if (cur
->_bf
== 1)
RotateL(parent
);
else if (cur
->_bf
== -1)
RotateRL(parent
);
}
else if (parent
->_bf
== -2)
{
if (cur
->_bf
== 1)
RotateLR(parent
);
else if (cur
->_bf
== -1)
RotateR(parent
);
}
break;
}
}
return true;
}
void RotateL(Node
* parent
)
{
Node
* subR
= parent
->_right
;
Node
* subRL
= subR
->_left
;
parent
->_right
= subRL
;
if (subRL
)
{
subRL
->_parent
= parent
;
}
subR
->_left
= parent
;
Node
* ppNode
= parent
->_parent
;
parent
->_parent
= subR
;
if (_root
== parent
)
{
_root
= subR
;
subR
->_parent
= nullptr;
}
else
{
subR
->_parent
= ppNode
;
if (ppNode
->_left
==parent
)
ppNode
->_left
= subR
;
else
ppNode
->_right
= subR
;
}
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
;
}
subL
->_right
= parent
;
Node
* ppNode
= parent
->_parent
;
parent
->_parent
= subL
;
if (_root
== parent
)
{
_root
= subL
;
subL
->_parent
= nullptr;
}
else
{
subL
->_parent
= ppNode
;
if (ppNode
->_left
== parent
)
ppNode
->_left
= subL
;
else
ppNode
->_right
= subL
;
}
parent
->_bf
= subL
->_bf
= 0;
}
void RotateRL(Node
* parent
)
{
Node
* subR
= parent
->_right
;
Node
* subRL
= subR
->_left
;
int bf
= subRL
->_bf
;
RotateR(subR
);
RotateL(parent
);
if (bf
== -1)
{
subR
->_bf
= 1;
parent
->_bf
= 0;
subRL
->_bf
= 0;
}
else if (bf
== 1)
{
parent
->_bf
= -1;
subR
->_bf
= 0;
subRL
->_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)
{
subL
->_bf
= 0;
parent
->_bf
= 1;
subLR
->_bf
= 0;
}
else if (bf
== 1)
{
parent
->_bf
= 0;
subL
->_bf
= -1;
subLR
->_bf
= 0;
}
}
void _InOrder(Node
* root
)
{
if (root
== nullptr)
return;
_InOrder(root
->_left
);
cout
<< root
->_kv
.first
<< ":" << root
->_kv
.second
<< endl
;
_InOrder(root
->_right
);
}
void InOrder()
{
_InOrder(_root
);
}
int Height(Node
* root
)
{
if (root
== nullptr)
return 0;
int leftHeight
= Height(root
->_left
);
int rightHeight
= Height(root
->_right
);
return leftHeight
> rightHeight
? leftHeight
+ 1 : rightHeight
+ 1;
}
bool _IsBalance(Node
* root
)
{
if (root
== nullptr)
return true;
int leftHeight
= Height(root
->_left
);
int rightHeight
= Height(root
->_right
);
return abs(leftHeight
- rightHeight
) < 2
&& _IsBalance(root
->_left
)
&& _IsBalance(root
->_right
);
}
bool IsBalance()
{
return _IsBalance(_root
);
}
private:
Node
* _root
= nullptr;
};
void TestAVLTree()
{
int a
[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
AVLTree
<int, int> t
;
for (auto e
: a
)
{
t
.Insert(make_pair(e
, e
));
}
t
.InOrder();
cout
<< t
.IsBalance() << endl
;
}
总结
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。
下面是我对关于左旋右旋,左右双旋的简单记忆方法:
左旋:找父亲右结点,再从右节点找右左结点 右旋:找父亲左结点,再从左结点找左右结点
右左双旋:先找右结点,通过右找右左结点 ,设定这个结点的bf if(bf==-1) subR->_bf=1; else if(bf==1) parent->_bf=-1;
左右双旋:先找左结点,通过左找左右结点 ,设定这个结点的bf if(bf==-1) parent->_bf=1; else if(bf==-1) subL->_bf=-1;