由于二叉搜索树的不平衡性会导致搜索的效率降低(比如单支情况),所以在将二叉搜索树升级改造成为了二叉平衡树。
AVL树是一个“加上了额外平衡条件”的二叉搜索树,其平衡为为了保持整个树的深度为O(logN),直观上的最佳平衡状态是每个节点的左右子树的高度相同,但是这个条件过于严苛,所以AVL树退而求次之,它的要求就是任何节点 的左右子树高度相差最多1,虽然这是一个比较弱的条件,但是足够保持树的平衡了。
所以说二叉平衡树只是二叉搜索树的基础上加入了平衡因子而改造而来的。
1.首先给出树的定义的结点,注意和搜索树有所不同,里面多了平衡因子_bf和指向父结点的指针(调整保持平衡性的时候要用)
template<class K,class V>
struct AVLBinTreeNode
{
AVLBinTreeNode<K, V> *_pLeft;
AVLBinTreeNode<K, V>* _pRight;
AVLBinTreeNode<K, V>* _pParent;
K _key;
V _value;
int _bf;
AVLBinTreeNode(const K& key, const V& value)
:_pLeft(NULL)
, _pRight(NULL)
, _pParent(NULL)
, _key(key)
, _value(value)
, _bf(0){}
};
2.插入和二叉搜索树的插入一样,只不过插入完了以后要计算出每个结点的平衡因子,并且根据平衡因子判断是否平衡,如果不平衡则需要调整(专业的来说就是旋转)
举个例子说明问题:
可以看出插入结点11这个结点以后,这个结点违反了AVL的平衡条件,由于只有“插入结点至根节点”路径上的各节点可能改变平衡状态,因此只需要调整其中最深的那个结点,可使整个树重新获得平衡。假设该最深的结点为X,由于结点最多拥有两个子节点,而所谓“平衡被破坏”意味着X的左右两棵子树的高度相差2,所以通过这个结论我们可以将插入的结点分为如下四四种情况
1.插入点位于X的左子结点的左子树上----->左左
2.插入点位于X的右子结点的右子树上----->右右
3.插入点位于X的左子结点的右子树上----->左右
4.插入点位于X的右子结点的左子树上----->右左
对于这四种情况你先脑子里大概想一下,下面我来一个一个介绍
1.左左使用右单旋
2.右右使用左单旋
3.左右,先左旋在右旋(双旋)
4.右左,先右旋在左旋(双旋)
这里就不列举了,因为他和左右也是一致对称的。留给你自己去画图了!
再次强调一遍:想学会AVL的树的旋转,需要明白1.插入一个结点后会导致这个结点从下向上的一条路径上的所有的结点的平衡状态都有可能改变,2.只需要调节那个最深的结点就可以使树平衡,这里的最深的结点的标志就是平衡因子等于2。。。。3.因为每个结点最多就一个左子节点一个右子节点,如果没有插入的话是平衡的,一旦插入要么在左子结点的左子树,要么在左子结点的右子树,要么在右子结点的右子树上,要么在右子节点的左子树上,就是上面所说的四种情况。自己好好画图理解。
下面给出每个旋转的代码自己也可以结合代码去理解
1.左旋代码
void _RotateL(PNode pParent)//左旋
{
PNode SubR = pParent->_pRight;
PNode SubRL = SubR->_pLeft;
pParent->_pRight = SubRL;
if (SubRL)
{
SubRL->_pParent = pParent;
}
PNode ppNode =pParent->_pParent;
SubR->_pLeft = pParent;
pParent->_pParent = SubR;
if (ppNode == NULL)
{
_pRoot = SubR;
SubR->_pParent = NULL;
}
else
{
if (ppNode->_pLeft == pParent)
{
ppNode->_pLeft = SubR;
}
else
{
ppNode->_pRight = SubR;
}
SubR->_pParent = ppNode;
}
pParent->_bf=SubR->_bf = 0;
}
2.右旋代码
void _RotateR(PNode pParent)
{
PNode SubL = pParent->_pLeft;
PNode SubLR = SubL->_pRight;
pParent->_pLeft = SubLR;
if (SubLR)
{
SubLR->_pParent = pParent;
}
PNode ppNode = pParent->_pParent;
SubL->_pRight = pParent;
pParent->_pParent = SubL;
if (ppNode == NULL)
{
_pRoot = SubL;
SubL->_pParent = NULL;
}
else
{
if (ppNode->_pLeft == pParent)
{
ppNode->_pLeft = SubL;
SubL->_pParent = ppNode;
}
else
{
ppNode->_pRight = SubL;
SubL->_pParent = ppNode;
}
}
pParent->_bf = SubL->_bf = 0;
}
3.左右旋代码
void _RotateLR(PNode pParent)
{
PNode SubL = pParent->_pLeft;
PNode SubLR = SubL->_pRight;
int bf = SubLR->_bf;
_RotateL(SubL);
_RotateR(pParent);
if (bf == 0)//如果bf是0等于插入结点就是SubLR,这个时候所有旋转的bf都是0
{
SubL->_bf = pParent->_bf = 0;
}
else if (bf == -1)//在 bf的左子树树插入新结点
{
SubL->_bf = -1;
pParent->_bf = 0;
}
else//新结点的右子树树插入结点
{
SubL->_bf = 0;
pParent->_bf = -1;
}
SubLR->_bf = 0;
}
4.右左旋代码
void _RotateRL(PNode pParent)
{
PNode SubR = pParent->_pRight;
PNode SubRL= SubR->_pLeft;
int bf = SubRL->_bf;
_RotateR(SubR);
_RotateL(pParent);
if (bf == 0)//如果bf是0等于插入结点就是SubLR,这个时候所有旋转的bf都是0
{
SubR->_bf = pParent->_bf = 0;
}
else if (bf == -1)//在 bf的左子树树插入新结点
{
SubR->_bf = 0;
pParent->_bf = -1;
}
else//新结点的右子树树插入结点
{
SubR->_bf = -1;
pParent->_bf = 0;
}
SubRL->_bf = 0;
}
下面给出AVL树的插入代码,融合了平衡调整
bool Insert(const K& key, const V& value)
{
if (_pRoot == NULL)
{
_pRoot = new Node(key, value);
return true;
}
PNode pParent = NULL;
PNode cur = _pRoot;
while (cur)//找位置
{
if (cur->_key == key)
return false;
else if (cur->_key > key)
{
pParent = cur;
cur = cur->_pLeft;
}
else
{
pParent = cur;
cur = cur->_pRight;
}
}
PNode pNewNode = new Node(key, key);
if (pParent->_key > key)
{
pParent->_pLeft = pNewNode;
pNewNode->_pParent = pParent;
}
else
{
pParent->_pRight = pNewNode;
pNewNode->_pParent = pParent;
}
//插入以后要开始调整
while (pParent)
{
//首先计算父结点平衡因子
if (pParent->_pLeft == pNewNode)//插入在当前结点的左边则bf--
{
pParent->_bf--;
}
else
{
pParent->_bf++;
}
//根据父结点平衡因子判断是否需要调整
if (pParent->_bf == 0)//平衡
{
return true;
}
else if (pParent->_bf==1||pParent->_bf==-1)//也是平衡,往回找判断
{
pNewNode = pParent;
pParent = pParent->_pParent;
}
else if (pParent->_bf == 2 || pParent->_bf == -2)
{
if (pParent->_bf == 2)//左边的结点比较多
{
if (pNewNode->_bf == 1)
{
_RotateL(pParent);//
return true;
}
else
{
_RotateRL(pParent);//右左旋转
return true;
}
}
if (pParent->_bf == -2)
{
if (pNewNode->_bf == -1)
{
_RotateR(pParent);
return true;
}
else
{
_RotateLR(pParent);
return true;
}
}
}
}
return true;
}
剩下的关于AVL树的就没有什么值得多花时间去啰嗦了下面我将直接给出代码,分别有1.判断平衡树是否平衡,2.求平衡树的高度,3中序遍历平衡二叉树
void _InOrder(PNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_key << " ";
_InOrder(pRoot->_pRight);
}
}
bool _IsBalance_one(PNode pRoot)
{
if (pRoot == NULL)
{
return true;
}
int height = abs(_Height(pRoot->_pRight) - _Height(pRoot->_pLeft));
return (height < 2 )&& _IsBalance_one(pRoot->_pLeft) && _IsBalance_one(pRoot->_pRight);
}
bool _IsBalance_two(PNode pRoot,int &depth)//自下向上的方法
{
if (NULL == pRoot)
{
depth = 0;
return true;
}
int leftDepth, rightDepth;
if (_IsBalance_two(pRoot->_pLeft, leftDepth) == false)
{
return false;
}
if (_IsBalance_two(pRoot->_pRight, rightDepth) == false)
{
return false;
}
if (rightDepth - leftDepth != pRoot->_bf)
{
cout << "bf异常" << pRoot->_bf << endl;
}
depth = leftDepth > rightDepth ? (leftDepth + 1) : (rightDepth + 1);
return true;
}
int _Height(PNode pRoot)
{
if (NULL == pRoot)
{
return 0;
}
int lheight = _Height(pRoot->_pLeft) + 1;//求出左子树的高度
int rheight = _Height(pRoot->_pRight) + 1;//求出右子树的高度
return lheight > rheight ? lheight : rheight;
}
下面给出完整的代码包括测试案例
#include<iostream>
using namespace std;
template<class K,class V>
struct AVLBinTreeNode
{
AVLBinTreeNode<K, V> *_pLeft;
AVLBinTreeNode<K, V>* _pRight;
AVLBinTreeNode<K, V>* _pParent;
K _key;
V _value;
int _bf;
AVLBinTreeNode(const K& key, const V& value)
:_pLeft(NULL)
, _pRight(NULL)
, _pParent(NULL)
, _key(key)
, _value(value)
, _bf(0){}
};
template<class K,class V>
class AVLBinTree
{
public:
typedef AVLBinTreeNode<K, V> Node;
typedef Node* PNode;
private:
PNode _pRoot;
public:
AVLBinTree()
:_pRoot(NULL){}
bool Insert(const K& key, const V& value)
{
if (_pRoot == NULL)
{
_pRoot = new Node(key, value);
return true;
}
PNode pParent = NULL;
PNode cur = _pRoot;
while (cur)//找位置
{
if (cur->_key == key)
return false;
else if (cur->_key > key)
{
pParent = cur;
cur = cur->_pLeft;
}
else
{
pParent = cur;
cur = cur->_pRight;
}
}
PNode pNewNode = new Node(key, key);
if (pParent->_key > key)
{
pParent->_pLeft = pNewNode;
pNewNode->_pParent = pParent;
}
else
{
pParent->_pRight = pNewNode;
pNewNode->_pParent = pParent;
}
//插入以后要开始调整
while (pParent)
{
//首先计算父结点平衡因子
if (pParent->_pLeft == pNewNode)//插入在当前结点的左边则bf--
{
pParent->_bf--;
}
else
{
pParent->_bf++;
}
//根据父结点平衡因子判断是否需要调整
if (pParent->_bf == 0)//平衡
{
return true;
}
else if (pParent->_bf==1||pParent->_bf==-1)//也是平衡,往回找判断
{
pNewNode = pParent;
pParent = pParent->_pParent;
}
else if (pParent->_bf == 2 || pParent->_bf == -2)
{
if (pParent->_bf == 2)//左边的结点比较多
{
if (pNewNode->_bf == 1)
{
_RotateL(pParent);//
return true;
}
else
{
_RotateRL(pParent);//右左旋转
return true;
}
}
if (pParent->_bf == -2)
{
if (pNewNode->_bf == -1)
{
_RotateR(pParent);
return true;
}
else
{
_RotateLR(pParent);
return true;
}
}
}
}
return true;
}
void _RotateL(PNode pParent)//左旋
{
PNode SubR = pParent->_pRight;
PNode SubRL = SubR->_pLeft;
pParent->_pRight = SubRL;
if (SubRL)
{
SubRL->_pParent = pParent;
}
PNode ppNode =pParent->_pParent;
SubR->_pLeft = pParent;
pParent->_pParent = SubR;
if (ppNode == NULL)
{
_pRoot = SubR;
SubR->_pParent = NULL;
}
else
{
if (ppNode->_pLeft == pParent)
{
ppNode->_pLeft = SubR;
}
else
{
ppNode->_pRight = SubR;
}
SubR->_pParent = ppNode;
}
pParent->_bf=SubR->_bf = 0;
}
void _RotateR(PNode pParent)
{
PNode SubL = pParent->_pLeft;
PNode SubLR = SubL->_pRight;
pParent->_pLeft = SubLR;
if (SubLR)
{
SubLR->_pParent = pParent;
}
PNode ppNode = pParent->_pParent;
SubL->_pRight = pParent;
pParent->_pParent = SubL;
if (ppNode == NULL)
{
_pRoot = SubL;
SubL->_pParent = NULL;
}
else
{
if (ppNode->_pLeft == pParent)
{
ppNode->_pLeft = SubL;
SubL->_pParent = ppNode;
}
else
{
ppNode->_pRight = SubL;
SubL->_pParent = ppNode;
}
}
pParent->_bf = SubL->_bf = 0;
}
void _RotateLR(PNode pParent)
{
PNode SubL = pParent->_pLeft;
PNode SubLR = SubL->_pRight;
int bf = SubLR->_bf;
_RotateL(SubL);
_RotateR(pParent);
if (bf == 0)//如果bf是0等于插入结点就是SubLR,这个时候所有旋转的bf都是0
{
SubL->_bf = pParent->_bf = 0;
}
else if (bf == -1)//在 bf的左子树树插入新结点
{
SubL->_bf = -1;
pParent->_bf = 0;
}
else//新结点的右子树树插入结点
{
SubL->_bf = 0;
pParent->_bf = -1;
}
SubLR->_bf = 0;
}
void _RotateRL(PNode pParent)
{
PNode SubR = pParent->_pRight;
PNode SubRL= SubR->_pLeft;
int bf = SubRL->_bf;
_RotateR(SubR);
_RotateL(pParent);
if (bf == 0)//如果bf是0等于插入结点就是SubLR,这个时候所有旋转的bf都是0
{
SubR->_bf = pParent->_bf = 0;
}
else if (bf == -1)//在 bf的左子树树插入新结点
{
SubR->_bf = 0;
pParent->_bf = -1;
}
else//新结点的右子树树插入结点
{
SubR->_bf = -1;
pParent->_bf = 0;
}
SubRL->_bf = 0;
}
void InOrder()
{
_InOrder(_pRoot);
}
bool IsBalance_one()//判断是否为平衡二叉树
{
return _IsBalance_one(_pRoot);
}
bool IsBalance_two()
{
int depth;
return _IsBalance_two(_pRoot,depth);
}
int Height()//求树的高度
{
return _Height(_pRoot);
}
private:
void _InOrder(PNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_key << " ";
_InOrder(pRoot->_pRight);
}
}
bool _IsBalance_one(PNode pRoot)
{
if (pRoot == NULL)
{
return true;
}
int height = abs(_Height(pRoot->_pRight) - _Height(pRoot->_pLeft));
return (height < 2 )&& _IsBalance_one(pRoot->_pLeft) && _IsBalance_one(pRoot->_pRight);
}
bool _IsBalance_two(PNode pRoot,int &depth)//自下向上的方法
{
if (NULL == pRoot)
{
depth = 0;
return true;
}
int leftDepth, rightDepth;
if (_IsBalance_two(pRoot->_pLeft, leftDepth) == false)
{
return false;
}
if (_IsBalance_two(pRoot->_pRight, rightDepth) == false)
{
return false;
}
if (rightDepth - leftDepth != pRoot->_bf)
{
cout << "bf异常" << pRoot->_bf << endl;
}
depth = leftDepth > rightDepth ? (leftDepth + 1) : (rightDepth + 1);
return true;
}
int _Height(PNode pRoot)
{
if (NULL == pRoot)
{
return 0;
}
int lheight = _Height(pRoot->_pLeft) + 1;//求出左子树的高度
int rheight = _Height(pRoot->_pRight) + 1;//求出右子树的高度
return lheight > rheight ? lheight : rheight;
}
};
int main()
{
int a[] = {18,14,20,12,16,15};
AVLBinTree< int, int> bst;
for (int i = 0; i < sizeof(a) / sizeof(*a); ++i)
{
bst.Insert(a[i], i);
}
bst.InOrder();
cout << bst.Height();
cout << bst.IsBalance_one();
cout << bst.IsBalance_two();
system("pause");
return 0;
}
代码已经上传至github:
github 链接