AVL树
概念
搜索二叉树虽然降低了查找的效率,但是如果数据有序或者接近有序,那么搜索二叉树就会退化成单枝树,搜索的时间复杂度就会变成O(n),极大的降低了效率,因此我们引入了AVL树,在向树中插入元素的时候,能保证每一个节点的左右子树的高度不会相差1,从而降低了树的高度,提高了查找的效率。
2.我们把具有一下性质的树称之为AVL树:
1. 首先它是二叉搜索树。
2. 二叉树每个节点的左子树和右子树都是AVL树。
3. 每一个节点的平衡因子绝对值不超过1(平衡因子:当前节点右子树高度减去左子树高度)
这样就可以降低树的高度,使得AVL树的查找时间复杂度在O(log(n));
AVL树的插入操作
插入操作很有可能导致AVL树不满足性质,所以我们需要旋转操作来使得树满足性质。
具体插入过程:
- 当树是空树的时候,直接创建节点,此节点为根节点,满足性质
- 当树非空时,先查找当前节点是否在树中存在,若存在,插入失败,直接返回,如果不存在,找到插入位置,插入节点,更新平衡因子,调整树。
新节点pcur的平衡因子为0,它的双亲的平衡因子有三种情况:
1.双亲parent的平衡因子为0
这种情况就是在parent的较矮的子树上插入,那么parent的平衡因子为0,parent平衡,从parent到根节点的路径上的节点的平衡因子都没有发生改变,因此树满足AVL树性质,不需调整。
2.双亲的parent的平衡因子绝对值为1
parent节点平衡因子在插入之前为0,插入之后以parent为根的树平衡没有被破坏,但是子树的高度增加了,向上回溯查看节点平衡因子。
3.在上述2更新完成后,如果parent平衡因子的绝对值为2,新节点在较高子树上进行插入,那么就必须要进行平衡处理:
如果parent->_buf是2,说明右子树高,设parent的右子树为subR,那么
当subR->_buf为1时候,执行左单旋转:
当subR->buf为-1的时候,执行先右后左双旋;
如果pparent->_buf是-2,说明左子树高,折parent的左子树是subL,那么
当subL->buf是-1的时候,执行右单旋;
当subR->buf是1的时候,执行先左后右双旋;
旋转之后parent为跟的子树高度降低,不需要向上进行回溯。
左单旋
插入节点位于较高右子树的右侧
void RotateL(pNode pParent)
{
pNode subR = pParent->_pRight;
pNode subRL = pParent->_pRight->_pLeft;
pParent->_pRight = subRL;
if (subRL)
subRL->_pParent = pParent;
subR->_pLeft = pParent;
pNode pparent = pParent->_pParent;
pParent->_pParent = subR;
subR->_pParent = pparent;
if (pParent == _pRoot)
{
_pRoot = subR;
}
else
{
if (pparent->_pLeft == pParent)
pparent->_pLeft = subR;
else
pparent->_pRight = subR;
}
pParent->_buf = subR->_buf = 0;
}
右单旋
插入节点位于较高左子树的左侧
void RotateR(pNode pParent)
{
pNode subL = pParent->_pLeft;
pNode subLR = pParent->_pLeft->_pRight;
pParent->_pLeft = subLR;
if (subLR)
subLR->_pParent = pParent;
subL->_pRight = pParent;
pNode pparent = pParent->_pParent;
pParent->_pParent = subL;
subL->_pParent = pparent;
if (pParent == _pRoot)
_pRoot = subL;
else
{
if (pparent->_pLeft == pParent)
pparent->_pLeft = subL;
else
pparent->_pRight = subL;
}
pParent->_buf = subL->_buf = 0;
}
右左双旋
插入节点位于较高右子树的左侧
void RotateRL(pNode pParent)
{
pNode subR = pParent->_pRight;
pNode subRL = NULL;
if (subR != NULL)
subRL = subR->_pLeft;
RotateR(subR);
RotateL(pParent);
if (subRL->_buf == 1)
pParent->_buf = -1;
else if (subRL->_buf == -1)
pParent->_buf = 0;
}
左右双旋
插入节点位于较高左子树的右侧
void RotateLR(pNode pParent)
{
pNode subL = pParent->_pLeft;
pNode subLR = NULL;
if (subL != NULL)
subLR = subL->_pRight;
RotateL(subL);
RotateR(pParent);
if (subLR->_buf == 1)
pParent->_buf = 0;
else if (subLR->_buf == -1)
pParent->_buf = 1;
}
AVL树的删除
- 从AVL树中删除一个节点,首先要判断这个节点是否在树中,若存在,删除就可能导致AVL树不满足性质的情况,就要进行旋转调整。
- 被删除节点存在一下几种情况:
1.删除节点只有左孩子。
2.删除节点只有右孩子。
3.删除节点左右孩子都存在。—->变为删除中序遍历下的第一个节点。 - 更新parent的平衡因子,如果删除节点是parent 的左孩子,那么平衡因子+1,相反,如果是右孩子,平衡因子-1,根据修改后的parent的平衡因子修改到根节点的所有节点。
1.如果parent的平衡因子为1或者-1,那么parent的高度不变,以parent为子树的所有节点平衡因子都没有发生变化,不需要调整。
2.如果parent的平衡因子变成0,虽然以parent为根的树平衡了,但是其高度减1了,这时候就要去检查parent的双亲的平衡因子的情况。
3.parent的平衡因子为2或者-2,则较矮的子树被缩短,需要进行平衡话旋转。
令parent较高子树的根为q,根据q的平衡因子,分一下三种情况 :
如果q的平衡因子为0,执行单旋转恢复parent
如果q的平衡因子与parent平衡因子(正负)号相同,则执行一个单旋转恢复 parent
如果q的平衡因子与parent平衡因子(正负)号相反,则执行一个双旋转恢复 parent
AVL树的代码:
#pragma once
#include<iostream>
#include<math.h>
using namespace std;
template<class k, class v>
struct AVLTreeNode
{
AVLTreeNode(const k& key, const v& value)
:_key(key)
,_value(value)
,_buf(0)
,_pLeft(NULL)
,_pRight(NULL)
,_pParent(NULL)
{}
AVLTreeNode<k, v>* _pLeft;
AVLTreeNode<k, v>* _pRight;
AVLTreeNode<k, v>* _pParent;
k _key;
v _value;
int _buf;
};
template<class k,class v>
class AVLTree
{
typedef AVLTreeNode<k, v> Node;
typedef Node* pNode;
public:
AVLTree()
:_pRoot(NULL)
{}
bool Insert(const k& key, const v&value)
{
if (_pRoot == NULL)
{
_pRoot = new Node(key, value);
return true;
}
pNode pCur = _pRoot;
pNode pParent = NULL;
while (pCur)
{
if (pCur->_key > key)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else if (pCur->_key < key)
{
pParent = pCur;
pCur = pCur->_pRight;
}
else
{
cout << "元素已存在,插入失败" << endl;
return false;
}
}
pCur = new Node(key, value);
if (pParent->_key > key)
{
pParent->_pLeft =pCur;
pParent->_buf--;
}
else
{
pParent->_pRight = pCur;
pParent->_buf++;
}
pCur->_pParent = pParent;
//插入成功,接下来就是进行调整。
//情况一,如果pParent的平衡因子是0
while (pParent)
{
if (pParent->_pRight == pCur)
pParent->_buf++;
else
pParent->_buf--;
if (pParent->_buf == 0)
break;
else if (pParent->_buf == 1 || pParent->_buf == -1)
{
pCur = pParent;
pParent = pCur->_pParent;
}
else
{
if (pParent->_buf == 2)
{
if (pCur->_buf == 1)
RotateL(pParent);
else if (pCur->_buf == -1)
RotateRL(pParent);
}
else if (pParent->_buf == -2)
{
if (pCur->_buf == -1)
RotateR(pParent);
else if (pCur->_buf == 1)
RotateLR(pParent);
}
break;
}
}
return true;
}
void InOrder()
{
_InOrder(_pRoot);
}
bool IsBalance()
{
return _IsBalance(_pRoot);
}
private:
bool _IsBalance(pNode pRoot)
{
if (pRoot == NULL)
return true;
int lefttree = _IsBalance(pRoot->_pLeft);
int righttree = _IsBalance(pRoot->_pRight);
if (abs(righttree - lefttree) > 1)
return false;
return _IsBalance(pRoot->_pLeft) && _IsBalance(pRoot->_pRight);
}
int _Height(pNode pRoot)
{
if (pRoot == NULL)
return 0;
int left = _Height(pRoot->_pLeft);
int right = _Height(pRoot->_pRight);
return left > right ? left + 1 : right + 1;
}
void RotateL(pNode pParent)
{
pNode subR = pParent->_pRight;
pNode subRL = pParent->_pRight->_pLeft;
pParent->_pRight = subRL;
if (subRL)
subRL->_pParent = pParent;
subR->_pLeft = pParent;
pNode pparent = pParent->_pParent;
pParent->_pParent = subR;
subR->_pParent = pparent;
if (pParent == _pRoot)
{
_pRoot = subR;
}
else
{
if (pparent->_pLeft == pParent)
pparent->_pLeft = subR;
else
pparent->_pRight = subR;
}
pParent->_buf = subR->_buf = 0;
}
void RotateR(pNode pParent)
{
pNode subL = pParent->_pLeft;
pNode subLR = pParent->_pLeft->_pRight;
pParent->_pLeft = subLR;
if (subLR)
subLR->_pParent = pParent;
subL->_pRight = pParent;
pNode pparent = pParent->_pParent;
pParent->_pParent = subL;
subL->_pParent = pparent;
if (pParent == _pRoot)
_pRoot = subL;
else
{
if (pparent->_pLeft == pParent)
pparent->_pLeft = subL;
else
pparent->_pRight = subL;
}
pParent->_buf = subL->_buf = 0;
}
void RotateRL(pNode pParent)
{
pNode subR = pParent->_pRight;
pNode subRL = NULL;
if (subR != NULL)
subRL = subR->_pLeft;
RotateR(subR);
RotateL(pParent);
if (subRL->_buf == 1)
pParent->_buf = -1;
else if (subRL->_buf == -1)
pParent->_buf = 0;
}
void RotateLR(pNode pParent)
{
pNode subL = pParent->_pLeft;
pNode subLR = NULL;
if (subL != NULL)
subLR = subL->_pRight;
RotateL(subL);
RotateR(pParent);
if (subLR->_buf == 1)
pParent->_buf = 0;
else if (subLR->_buf == -1)
pParent->_buf = 1;
}
void _InOrder(pNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << "<" << pRoot->_key << "," << pRoot->_value << ">" << endl;
_InOrder(pRoot->_pRight);
}
}
private:
pNode _pRoot;
};
void TestAVLTreee()
{
int a[] = { 16,3,7,11,9,26,18,14,15 };
AVLTree<int, int> avl;
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
avl.Insert(a[i], i);
}
avl.InOrder();
}