在前面的文章中介绍了二叉搜索树,尽管它可以缩短查找的速率,但是在数据有序或者接近有序的情况下,二叉搜索树就会退化成一棵单支树,查找的效率就会变得低下。因此,就有两位伟大的数学家发明了一种新的数据结构——AVL树,解决了二叉搜索树的一些不足。
什么是AVL树?(概念)
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
1、它的左右子树都是AVL树
2、左子树和右子树高度之差(简称平衡因子)的绝对值不超过1(-1、0、1);
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在O(lgn),平均搜索时间复杂度O(lg(n))。
如图:
AVL树的插入:
①若是空树,则直接插入;
②若不是空树,寻找插入位置,若树中有相同的数据则不进行插入,直接返回;
③插入成功后要检查平衡因子,必须要满足平衡因子的绝对值小于2;
④更新平衡因子,对树进行调整;
在根据平衡因子调整树的情况分为一下几种:
假设新插入的节点为p(新插入的节点的平衡因子为0),其双亲节点为pr
情况一:结点Pr的平衡因子为0
在Pr的较矮的子树上插入新节点,结点Pr平衡,其高度没有增加,此时从Pr到根路径上各结点为根的子树的高度不变,即各结点的平衡因子不变,结束平衡化处理。
情况二:结点Pr的平衡因子的绝对值为1;
插入前Pr的平衡因子为0,插入后以Pr为根的子树没有失去平衡,但该子树的高度增加,需从该结点Pr向根节点方向回溯,继续查看Pr的双亲结点的平衡性。
情况三:结点Pr的平衡因子的绝对值为2(说明经过了情况二的回溯)新节点在较高的子树插入,需要做平衡化处理:
若Pr = 2,说明右子树高,设Pr的右子树为q;
当q的平衡因子为1,执行左单旋转;
当q的平衡因子为-1,执行先右后左双旋转;
若Pr = -2,说明左子树高,设Pr的左子树为q;
当q的平衡因子为-1,执行右单旋转;
当q的平衡因子为1,执行先左后右双旋转;
旋转后Pr为根的子树高度降低,无需继续向上层回溯;
接下来我们主要看这几种旋转的情况:
左单旋:
//左单旋
void _RotatelL(Node* parent)
{
assert(parent);
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
Node* pparent = parent->_parent;
parent->_right = SubRL;
if (SubRL)//判断SubRL是否为空
SubRL->_parent = parent;
SubR->_left = parent;
parent->_parent = SubR;
//判断parent是否为根节点
if (pparent == NULL)//parent是根节点
{
_root = SubR;
_root->_parent = NULL;
}
else//parent不是根节点
{
if (parent == pparent->_left)
{
pparent->_left = SubR;
}
else
{
pparent->_right = SubR;
}
SubR->_parent = pparent;
}
parent->bf = SubR->bf = 0;
}
右单旋:
//右单旋
void _RotatelR(Node* parent)
{
assert(parent);
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
Node* pparent = parent->_parent;
parent->_left = SubLR;
if (SubLR)
SubLR->_parent = parent;
SubL->_right = parent;
parent->_parent = SubL;
if (pparent == NULL)//如果parent是根节点
{
_root = SubL;
_root->_parent = NULL;
}
else//不是根节点
{
if (parent == pparent->_left)
pparent->_left = SubL;
else
pparent->_right = SubL;
SubL->_parent = pparent;
}
parent->bf = SubL->bf = 0;
}
左右双旋
左右双旋后,平衡因子有以下几种情况;
情况一:SubLR即为插入节点
情况二:插入的节点为SubLR的右节点
情况三:插入的节点为SubLR的左节点
//左右双旋
void _RotatelLR(Node* parent)
{
assert(parent);
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
int bf = SubLR->bf;
_RotatelL(SubL);//先左旋
_RotatelR(parent);//再右旋
if (bf == 0)//说明SubLR为新插入的节点
parent->bf = SubL->bf = 0;
else if (bf == 1)//说明插入的节点在SubLR的右边
{
SubL->bf = -1;
parent->bf = 0;
}
else//说明插入的节点在SubLR的左边
{
SubL->bf = 0;
parent->bf = 1;
}
SubLR->bf = 0;//最后,SubLR的孩子节点即左孩子或右孩子都分走了,因此要将SubLR的bf置0
}
左右双旋和单旋不同的是,平衡因子变化会与插入的节点位置有关即与SubLR的平衡因子有关,分为三种情况:①SubLR为插入点;②在SubLR的右边插入;③在SubLR的左边插入;这三种情况对应的SubLR的平衡因子有三种0、1和-1;因此对应parent和SubL的平衡因子就有三种情况:
右左双旋和左右双旋类似,这里就不多阐述。
以下是全部的源码:
#include<iostream>
#include<cassert>
using namespace std;
template<class T, class V>
struct AVLTreeNode
{
AVLTreeNode<T, V>* _left;
AVLTreeNode<T, V>* _right;
AVLTreeNode<T, V>* _parent;
T _key;
V _value;
int bf;//平衡因子
AVLTreeNode(const T& key, const V& value)
:_left(NULL)
, _right(NULL)
, _parent(NULL)
, _key(key)
, _value(value)
, bf(0)
{}
};
template<class T, class V>
class AVLTree
{
typedef AVLTreeNode<T, V> Node;
public:
AVLTree()
:_root(NULL)
{}
~AVLTree()
{
Destory(_root);
}
//插入
bool Insert(const T& key, const V& value)
{
//如果根节点,直接插入
if (_root == NULL)
{
_root = new Node(key,value);
return true;
}
//不是根节点
Node* cur = _root;
Node* parent = NULL;
//寻找插入位置
while (cur)
{
if (key < cur->_key)//向左
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)//向右
{
parent = cur;
cur = cur->_right;
}
else//树里已经有key,直接返回
{
return false;
}
}
//找到了要插入的位置
cur = new Node(key,value);
//判断要插入的位置在双亲节点的左/右
if (key < parent->_key)
{
parent->_left = cur;
cur->_parent = parent;
}
else
{
parent->_right = cur;
cur->_parent = parent;
}
//插入后需要更新平衡因子
while (parent)
{
//先更新双亲节点的平衡因子
if (parent->_left == cur)
parent->bf--;
else
parent->bf++;
//更新完后检查双亲的平衡因子是否平衡
if (parent->bf == 0)//平衡因子为0则平衡
return true;
else if (parent->bf == 1 || parent->bf == -1)//平衡因子绝对值为1,
{ //则要判断parent的双亲节点的平衡因子,进行向上回溯
cur = parent;
parent = parent->_parent;
}
else if (parent->bf == 2 || parent->bf == -2)//不平衡则进行调整
{
//分两种情况
//第一种
if (parent->bf == 2)
{
if (cur->bf == 1)//判断是否同号
{
_RotatelL(parent);//左旋
//return true;
}
else//cur->bf == -1 异号
{
_RotatelRL(parent);
//return true;
}
}
//第二种
else if (parent->bf == -2)
{
if (cur->bf == -1)//同号
{
_RotatelR(parent);
//return true;
}
else // 异号 cur->bf == 1
{
_RotatelLR(parent);
//return true;
}
}
break;
}
}
return true;
}
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
cur = cur->_left;
else if (key > cur->_key)
cur = cur->_right;
else
return true;
}
return false;
}
Node* Destory(Node* root)
{
if (root == NULL)
return NULL;
else
{
Destory(root->_left);
Destory(root->_right);
delete root;
root = NULL;
}
return root;
}
void Inorder()
{
_InOrder(_root);
cout << endl;
}
//判断平衡
bool Isbalance()
{
int hight = 0;
return _Isbalance(_root, hight);
}
private:
//左单旋
void _RotatelL(Node* parent)
{
assert(parent);
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
Node* pparent = parent->_parent;
parent->_right = SubRL;
if (SubRL)//判断SubRL是否为空
SubRL->_parent = parent;
SubR->_left = parent;
parent->_parent = SubR;
//判断parent是否为根节点
if (pparent == NULL)//parent是根节点
{
_root = SubR;
_root->_parent = NULL;
}
else//parent不是根节点
{
if (parent == pparent->_left)
{
pparent->_left = SubR;
}
else
{
pparent->_right = SubR;
}
SubR->_parent = pparent;
}
parent->bf = SubR->bf = 0;
}
//右单旋
void _RotatelR(Node* parent)
{
assert(parent);
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
Node* pparent = parent->_parent;
parent->_left = SubLR;
if (SubLR)
SubLR->_parent = parent;
SubL->_right = parent;
parent->_parent = SubL;
if (pparent == NULL)//如果parent是根节点
{
_root = SubL;
_root->_parent = NULL;
}
else//不是根节点
{
if (parent == pparent->_left)
pparent->_left = SubL;
else
pparent->_right = SubL;
SubL->_parent = pparent;
}
parent->bf = SubL->bf = 0;
}
//左右双旋
void _RotatelLR(Node* parent)
{
assert(parent);
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
int bf = SubLR->bf;
_RotatelL(SubL);//先左旋
_RotatelR(parent);//再右旋
if (bf == 0)//说明SubLR为新插入的节点
parent->bf = SubL->bf = 0;
else if (bf == 1)//说明插入的节点在SubLR的右边
{
SubL->bf = -1;
parent->bf = 0;
}
else//说明插入的节点在SubLR的左边
{
SubL->bf = 0;
parent->bf = 1;
}
SubLR->bf = 0;//最后,SubLR的孩子节点即左孩子或右孩子都分走了,因此要将SubLR的bf置0
}
//右左双旋
void _RotatelRL(Node* parent)
{
assert(parent);
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
int bf = SubRL->bf;
_RotatelR(SubR);
_RotatelL(parent);
if (bf == 0)//说明插入的节点是SubRL
parent->bf = SubR->bf = 0;
else if (bf == 1)//说明插入的节点在SubR的右边
{
SubR->bf = 0;
parent->bf = -1;
}
else//说明插入的节点在SubR的左边
{
SubR->bf = 1;
parent->bf = 0;
}
SubRL->bf = 0; //最后,SubRL的孩子节点即左孩子或右孩子都分走了,因此要将SubRL的bf置0
}
//中序遍历
void _InOrder(Node* root)
{
if (root)
{
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
}
//求树的高度
size_t _height(Node* root)
{
if (root == NULL)
return 0;
size_t hightL = _height(root->_left)+1;
size_t hightR = _height(root->_right) + 1;
return hightL > hightR ? hightL : hightR;
}
//判断平衡
bool _Isbalance(Node* root, int& hight)
{
if (root == NULL)
{
hight = 0;
return true;
}
int hightL = 0;
int hightR = 0;
if (_Isbalance(root->_left, hightL) == false)
return false;
if (_Isbalance(root->_right, hightR) == false)
return false;
if (hightR - hightL != root->bf)
{
cout << "平衡因子异常" <<root->_key<< endl;
}
hight = hightL > hightR ? hightL+1 : hightR+1;
return abs(hightL - hightR) < 2;
}
bool _Isbalance2(Node* root)
{
if (root == NULL)
return true;
int hightL = _Isbalance2(root->_left);
int hightR = _Isbalance2(root->_right);
return abs(hightL - hightR) < 2
&& _Isbalance2(root->_left)
&& _Isbalance2(root->_right);
}
private:
Node* _root;
};