一、概念
AVL树又称为高度平衡的二叉搜索树,它能保持二叉树的高度平衡,尽量降低二叉树的高度,减少树的平均搜索长度。
二、性质
- 树中每个左子树和右子树都是AVL树
- 左子树和右子树高度之差的绝对值(简称平衡因子)不超过1(0,1,-1)
平衡因子就是用右子树的高度减去左子树的高度
红色的就是每个节点的平衡因子
三、AVL树的插入
如果在一棵原本是平衡的二叉搜索树中插入一个新节点,可能造成不平衡,此时必须调整树 的结构,使之平衡化。
插入算法:
- 如果是空树,插入后即为根节点,插入后直接返回true
- 如果树不空,寻找插入位置,若在寻找的过程中找到key,则插入失败直接返回false
- 插入结点
- 更新平衡因子,对树进行调整
新节点pcur平衡因子为0,但其双亲结点parent的平衡因子有三种情况:
如果parent的平衡因子为0
即在parent较矮的子树上插入新节点,parent平衡,其高度没有增加,此时从parent到 根路径 上各结点为根的子树的高度不变,即各结点的平衡因子不变,结束平衡化处理。如果parent的平衡因子的绝对值为1
插入前parent的平衡因子为0,插入后以parent为根的子树没有失去平衡,但该子树 的高度增 加,需从parent向根节点方向回溯,继续查看parent的双亲的平衡性。- 在上述更新后,如果parent平衡因子的绝对值为2,新节点在较高的子树插入,需要做平衡化处理:
若parent->_bf == 2,说明右子树高,设parent的右子subR
当subR的平衡因子为1,执行左单旋转
当subR的平衡因子为-1,执行先右后左双旋转
若parent->_bf == -2,说明左子树高,设parent的左子树subL
当subL的平衡因子为-1,执行右单旋转
当subL的平衡因子为1,执行先左后右双旋转
旋转后parent为根的子树高度降低,无需继续向上层回溯
1.左单旋
插入节点位于较高右子树的右侧
如下图所示:
//左单旋
void RotateL(pNode parent)
{
pNode subR = parent->_pRightChild;
pNode subRL = subR->_pLeftChild;
parent->_pRightChild = subRL;
if (subRL)
subRL->_pParent = parent;
subR->_pLeftChild = parent;
pNode pparent = parent->_pParent;
subR->_pParent = pparent;
parent->_pParent = subR;
if (parent == _pRoot)
{
_pRoot = subR;
}
else//更新祖父节点的左右孩子
{
if (pparent->_pLeftChild == parent)
pparent->_pLeftChild = subR;
else
pparent->_pRightChild = subR;
}
//更新平衡因子
parent->_bf = subR->_bf = 0;
}
2.右单旋
插入节点位于较高左子树的左侧
如下图所示:
void RotateR(pNode parent)
{
pNode subL = parent->_pLeftChild;
pNode subLR = subL->_pRightChild;
parent->_pLeftChild = subLR;
if (subLR)
subLR->_pParent = parent;
subL->_pRightChild = parent;
pNode pparent = parent->_pParent;
parent->_pParent = subL;
subL->_pParent = pparent;
if (parent == _pRoot)
{
_pRoot = subL;
}
else
{
if (pparent->_pLeftChild == parent)
pparent->_pLeftChild = subL;
else
pparent->_pRightChild = subL;
}
//更新平衡因子
parent->_bf = subL->_bf = 0;
}
3.左右双旋
插入节点在较高左子树的右侧
//左右双旋
void RotateLR(pNode parent)
{
pNode pSubL = parent->_pLeftChild;
pNode pSubLR = NULL;
if (pSubL)
pSubLR = pSubL->_pRightChild;
RotateL(parent->_pLeftChild);//先对parent的左孩子进行左旋
RotateR(parent);//对parent进行右旋
if (pSubLR->_bf == 1)
parent->_bf = 0;
else if (pSubLR->_bf == -1)
parent->_bf = 1;
}
4.右左双旋
插入节点位于较高右子树的左侧
//右左双旋
void RotateRL(pNode parent)
{
pNode pSubR = parent->_pRightChild;
pNode pSubRL = NULL;
if (pSubR!=NULL)
pSubRL = pSubR->_pLeftChild;
RotateR(parent->_pRightChild);
RotateL(parent);
if (pSubRL->_bf == 1)
parent->_bf = -1;
else if (pSubRL->_bf == -1)
parent->_bf = 0;
}
4.AVL的删除
(在这里简单介绍一下删除的算法,不做具体的代码实现)
从AVL树中删除一个节点,首先必须检测该结点是否存在,若存在删除该结点之后可能会破 坏AVL树的高度平衡,因此需要做平衡化旋转。
被删除的结点pcur存在以下情况:
- 被删除结点只有左孩子
- 被删除结点只有右孩子
- 被删除结点左右孩子都存在–>变为删除中序遍历下的第一个结点q
更新parent的平衡因子,若q是parent的左孩子,则parent的平衡因子增加1,否则减少1,根据修改后的parent的平衡因子调整到根路径上的所有节点:
- parent平衡因子的平衡因子为1或-1,parent高度不变,则从parent到根所有节点高度均 不变,不用调整。
- parent的平衡因子变成0,虽然以parent为根的子树平衡,其高度减1,但需要检查 parent的双亲结点的平衡性。
- 结点parent的平衡因子为2或-2,则较矮子树被缩短,parent发生不平衡,需要进行平衡化旋转。
令parent较高子树的根为q,根据q的平衡因子,分一下三种情况
- 如果q的平衡因子为0,执行单旋转恢复parent
- 如果q的平衡因子与parent平衡因子(正负)号相同,则执行一个单旋转恢复 parent
- 如果q的平衡因子与parent平衡因子(正负)号相反,则执行一个双旋转恢复 parent
完整代码
#pragma once
#include <iostream>
using namespace std;
#include <stdlib.h>
template <class K,class V>
struct AVLTreeNode
{
AVLTreeNode(const K& key,const V& value)
:_pLeftChild(NULL)
, _pRightChild(NULL)
, _pParent(NULL)
, _key(key)
, _value(value)
, _bf(0)
{}
AVLTreeNode<K, V>* _pLeftChild;
AVLTreeNode<K, V>* _pRightChild;
AVLTreeNode<K, V>* _pParent;
K _key;
V _value;
int _bf;
};
template <class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
typedef AVLTreeNode<K, V>* pNode;
public:
AVLTree()
:_pRoot(NULL)
{}
//插入
bool Insert(const K& key, const V& value)
{
//空树的插入
if (_pRoot == NULL)
{
_pRoot = new Node(key, value);
return true;
}
pNode parent = NULL;
pNode cur = _pRoot;
//寻找待插入节点的位置
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_pLeftChild;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_pRightChild;
}
else
return false;
}
//插入节点
cur = new Node(key, value);
if (parent->_key < key)
{
parent->_pRightChild = cur;
cur->_pParent = parent;
}
else
{
parent->_pLeftChild = cur;
cur->_pParent = parent;
}
//调整树
while (parent)
{
if (parent->_pRightChild == cur)
parent->_bf++;
else
parent->_bf--;
if (parent->_bf == 0)
break;
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = cur->_pParent;
}
else
{
if (parent->_bf == 2)
{
if (cur->_bf == 1)//插入节点位于右孩子处
RotateL(parent);
else
RotateRL(parent);
}
else
{
if (cur->_bf == -1)
RotateR(parent);
else
RotateLR(parent);
}
break;
}
}
return true;
}
//中序遍历
void InOrder()
{
_InOrder(_pRoot);
cout << endl;
}
//判断是否为平衡二叉树
bool IsBalance()
{
return _IsBalance(_pRoot);
}
//求树的高度
int Height()
{
return _Height(_pRoot);
}
private:
void _InOrder(pNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeftChild);
cout << "<" << pRoot->_key << "," << pRoot->_value << ">" << endl;
_InOrder(pRoot->_pRightChild);
}
}
int _Height(pNode pRoot)
{
if (pRoot == NULL)
return 0;
int leftHeight = _Height(pRoot->_pLeftChild);
int rightHeight = _Height(pRoot->_pRightChild);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _IsBalance(pNode pRoot)
{
if (pRoot == NULL)
return true;
int leftHeight = _Height(pRoot->_pLeftChild);
int rightHeight = _Height(pRoot->_pRightChild);
if (abs(leftHeight - rightHeight) >= 2)
return false;
return _IsBalance(pRoot->_pLeftChild) && _IsBalance(pRoot->_pRightChild);
}
//左单旋
void RotateL(pNode parent)
{
pNode subR = parent->_pRightChild;
pNode subRL = subR->_pLeftChild;
parent->_pRightChild = subRL;
if (subRL)
subRL->_pParent = parent;
subR->_pLeftChild = parent;
pNode pparent = parent->_pParent;
subR->_pParent = pparent;
parent->_pParent = subR;
if (parent == _pRoot)
{
_pRoot = subR;
// subR->_pParent = NULL;
}
else//更新祖父节点的左右孩子
{
if (pparent->_pLeftChild == parent)
pparent->_pLeftChild = subR;
else
pparent->_pRightChild = subR;
}
parent->_bf = subR->_bf = 0;
}
//右单旋
void RotateR(pNode parent)
{
pNode subL = parent->_pLeftChild;
pNode subLR = subL->_pRightChild;
parent->_pLeftChild = subLR;
if (subLR)
subLR->_pParent = parent;
subL->_pRightChild = parent;
pNode pparent = parent->_pParent;
parent->_pParent = subL;
subL->_pParent = pparent;
if (parent == _pRoot)
{
_pRoot = subL;
// subL->_pParent = NULL;
}
else
{
if (pparent->_pLeftChild == parent)
pparent->_pLeftChild = subL;
else
pparent->_pRightChild = subL;
}
parent->_bf = subL->_bf = 0;
}
//右左双旋
void RotateRL(pNode parent)
{
pNode pSubR = parent->_pRightChild;
pNode pSubRL = NULL;
if (pSubR!=NULL)
pSubRL = pSubR->_pLeftChild;
RotateR(parent->_pRightChild);
RotateL(parent);
if (pSubRL->_bf == 1)
parent->_bf = -1;
else if (pSubRL->_bf == -1)
parent->_bf = 0;
}
//左右双旋
void RotateLR(pNode parent)
{
pNode pSubL = parent->_pLeftChild;
pNode pSubLR = NULL;
if (pSubL)
pSubLR = pSubL->_pRightChild;
RotateL(parent->_pLeftChild);
RotateR(parent);
if (pSubLR->_bf == 1)
parent->_bf = 0;
else if (pSubLR->_bf == -1)
parent->_bf = 1;
}
private:
pNode _pRoot;
};
测试代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include "AVLTree.h"
void Test()
{
int arr[10] = { 2, 5, 0, 6, 9, 1, 4, 3, 7, 8 };
AVLTree<int, int> avl;
for (int i = 0; i < 10; i++)
{
avl.Insert(arr[i], i);
}
avl.InOrder();
cout << "avl is balance?" << avl.IsBalance() << endl;
cout << "avl树高度为:" << avl.Height() << endl;
}
int main()
{
Test();
system("pause");
return 0;
}