1、AVL树
在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 “An algorithm for the organization of information” 中发表了它。
如果它有n个节点,那么其高度可保持在O(lg n),平均搜索时间复杂度为O(lg n);
特点:
- 也是特殊的二叉搜索树
- 它的左右子树都是AVL树
- 左子树和右子树高度之差(简称平衡因子)的绝对值不超过(-1,0,1)
2、平衡化旋转
如果在一棵原本是平衡的二叉搜索树中插入一个新节点,就有可能造成不平衡,这个时候就必须调整树的结构,使之平衡化。
如图所示:
调整失去平衡的AVL树可以对树进行旋转,根据不同的场景,大致可分为四种旋转:左单旋(LL),右单旋(RR),先左后右双旋(LR),先右后左双旋(RL)。
左单旋(LL):
插入点位于ptr的右子节点的右子树–右右
【调整方法】:
- 首先令ptr成为pSubR的左子树
- pSubRL的值是小于pSubR的值,大于ptr的值,所以可以令pSubRL作为ptr的右子树
- 判断ptr上一节点的三种情况
- 更新平衡因子(分析可得,树的其他节点的平衡因子都没有改变,只有ptr和pSubR的平衡因子变为0)
右单旋(RR):
插入点位于ptr的座子节点的左子树–左左
与左单旋十分相似
- 首先令ptr成为pSubL的右子树
- pSubLR的值是大于pSubL的值,小于ptr的值,所以可以令pSubLR作为ptr的左子树
- 判断ptr上一节点的三种情况
- 更新平衡因子
先左后右双旋(LR)
插入点位于ptr左子节点的右子树–左右
- 插入节点后,对不平衡节点(ptr)的左子树进行左单旋
- 对根为ptr的树进行右单旋
- 更新平衡因子
更新平衡因子的三种情况分析:
当bf == -1时:
当bf == 1时:
当bf == 0时:
树的各节点都平衡,各节点的平衡因子都为0,所以不用做任何处理。
先右后左双旋(RL)
插入点位于ptr右子节点的左子树–右左
- 插入节点后,对不平衡节点(ptr)的右子树进行右单旋
- 对根为ptr的树进行左单旋
- 更新平衡因子(更新平衡因子与LR大致相同,这里不做详细分析)
3、AVL树的插入
在AVL树中插入结点(key,value)的算法:
- 如果是空树,插入后即为根节点,插入后直接返回true
- 如果树不空,寻找插入位置,若在寻找的过程中找到key,则插入失败直接返回false
- 插入结点
- 更新平衡因子,对树进行调整
新节点pcur平衡因子为0,但其双亲结点parent的平衡因子有三种情况:
- 如果parent的平衡因子为0; 即在parent较矮的子树上插入新节点,parent平衡,其高度没有增加,此时从parent到根路径上各结点为根的子树的高度不变,即各结点的平衡因子不变,结束平衡化处理。
- 如果parent的平衡因子的绝对值为1;插入前parent的平衡因子为0,插入后以parent为根的子树没有失去平衡,但该子树的高度增加,需从parent向根节点方向回溯,继续查看parent的双亲的平衡性。
- 在上述2更新后,如果parent平衡因子的绝对值为2;新节点在较高的子树插入,需要做平衡化处理:
若parent->_bf == 2,说明右子树高,设parent的右子树为subR
当subR的平衡因子为1,执行左单旋转
当subR的平衡因子为-1,执行先右后左双旋转
若parent->_bf == -2,说明左子树高,设parent的左子树为subL
当subL的平衡因子为-1,执行右单旋转
当subL的平衡因子为1,执行先左后右双旋转
旋转后parent为根的子树高度降低,无需继续向上层回溯
AVLTree源码:
AVLTree.h
#pragma once
#include<iostream>
using namespace std;
template<class K,class V>
struct AVLTreeNode{
public:
AVLTreeNode()
{}
AVLTreeNode(const K& key,const V& value)
:_key(key)
, _value(value)
, _bf(0)
, _pLeft(NULL)
, _pRight(NULL)
, _pParent(NULL)
{}
public:
AVLTreeNode<K,V>* _pLeft;
AVLTreeNode<K,V>* _pRight;
AVLTreeNode<K,V>* _pParent;
int _bf;
K _key;
V _value;
};
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){
//如果是空树,插入后即为根节点,插入后直接返回true
if (NULL == _pRoot){
_pRoot = new Node(key, value);
return true;
}
//寻找插入位置,若在寻找的过程中找到key,则插入失败直接返回false
pNode pCur = _pRoot;
pNode pParent = NULL;
while (pCur){
if (key > pCur->_key){
pParent = pCur;
pCur = pCur->_pRight;
}
else if (key < pCur->_key){
pParent = pCur;
pCur = pCur->_pLeft;
}
else
return false;//在寻找的过程中找到key
}//pCur == NULL
//插入节点
pCur = new Node(key, value);
if (pParent->_key < pCur->_key)
{
pParent->_pRight = pCur;
}
else
{
pParent->_pLeft = pCur;
}
pCur->_pParent = pParent;
//更新平衡因子,调整树
while (pParent){//最多更新至根节点
//更新平衡因子,调整树
if (pCur == pParent->_pLeft){
pParent->_bf++;
}
if (pCur == pParent->_pRight){
pParent->_bf--;
}
//调整树
if (0 == pParent->_bf){
//各结点的平衡因子不变,结束平衡化处理
break;
}
else if (1 == pParent->_bf || -1 == pParent->_bf){
//以parent为根的子树没有失去平衡,但该子树的高度增加,需从parent向根节点方向回溯,继续查看parent的双亲的平衡性
pCur = pParent;
pParent = pParent->_pParent;
}
else{
//如果parent平衡因子的绝对值为2,新节点在较高的子树插入,需要做平衡化处理
if (-2 == pParent->_bf){
if (-1 == pCur->_bf){
//pParent->-2 pCur->-1 左单旋
RotateL(pParent);
}
if (1 == pCur->_bf){
//pParent->-2 pCur->1 先右后左双旋
RotateRL(pParent);
}
}
if (2 == pParent->_bf){
if (1 == pCur->_bf){
//pParent->2 pCur->1 右单旋
RotateR(pParent);
}
if (-1 == pCur->_bf){
//pParent->2 pCur->-1 先左后右双旋
RotateLR(pParent);
}
}
break;
}
}
return true;
}
//中序遍历
void InOder(){
cout << "中序遍历:"<< endl;
_InOder(_pRoot);
cout << endl;
}
//判断AVL树是否平衡
bool IsBalance(){
int depth;
return _IsBalance(_pRoot, depth);
}
//求取树的深度
int Depth(){
return _Depth(_pRoot);
}
protected:
//中序遍历
void _InOder(pNode pRoot){
if (NULL == pRoot)
return;
_InOder(pRoot->_pLeft);
cout << "<" << pRoot->_key << "," << pRoot->_value << ">" << endl;
_InOder(pRoot->_pRight);
}
//左单旋
void RotateL(pNode pParent){
if (NULL == pParent)
return;
pNode ptr = pParent;
pNode pSubR = ptr->_pRight;
pNode pSubRL = pSubR->_pLeft;
//标记ptr的父母节点
pNode pPParent = ptr->_pParent;
//令ptr成为pSubR的左子树
pSubR->_pLeft = ptr;
ptr->_pParent = pSubR;
//pSubRL的值是小于pSubR的值,大于ptr的值,所以可以令pSubRL作为ptr的右子树
ptr->_pRight = pSubRL;
if (pSubRL)
pSubRL->_pParent = ptr;
pSubR->_pParent = pPParent;
//判断ptr上一节点的三种情况
if (NULL == pPParent){
//ptr为根节点
_pRoot = pSubR;
}
else{
if (ptr == pPParent->_pLeft)
pPParent->_pLeft = pSubR;
else
pPParent->_pRight = pSubR;
}
//更新平衡因子
ptr->_bf = pSubR->_bf = 0;
}
//右单旋
void RotateR(pNode pParent){
if (NULL == pParent)
return;
pNode ptr = pParent;
pNode pSubL = ptr->_pLeft;
pNode pSubLR = pSubL->_pRight;
//标记ptr的父母节点
pNode pPParent = ptr->_pParent;
//令ptr成为pSubL的左子树
pSubL->_pRight = ptr;
ptr->_pParent = pSubL;
//pSubLR的值是大于pSubL的值,小于ptr的值,所以可以令pSubLR作为ptr的左子树
ptr->_pLeft = pSubLR;
if (pSubLR)
pSubLR->_pParent = ptr;
pSubL->_pParent = pPParent;
//判断ptr上一节点的三种情况
if (NULL == pPParent){
//ptr为根节点
_pRoot = pSubL;
}
else{
if (ptr == pPParent->_pLeft)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
//更新平衡因子
ptr->_bf = pSubL->_bf = 0;
}
//先左后右双旋(LR)
void RotateLR(pNode pParent){
if (NULL == pParent)
return;
pNode ptr = pParent;
pNode pSubL = ptr->_pLeft;
pNode pSubLR = pSubL->_pRight;
int bf = pSubLR->_bf;
//插入节点后,对不平衡节点(ptr)的左子树进行左单旋
RotateL(ptr->_pLeft);
//对根为ptr的树进行右单旋
RotateR(ptr);
//更新平衡因子
if (-1 == bf){
pSubL->_bf = 1;
}
else if (1 == bf){
ptr->_bf = -1;
}
else
{
;
}
}
//先右后左双旋(RL)
void RotateRL(pNode pParent){
if (NULL == pParent)
return;
pNode ptr = pParent;
pNode pSubR = ptr->_pRight;
pNode pSubRL = pSubR->_pLeft;
int bf = pSubRL->_bf;
//插入节点后,对不平衡节点(ptr)的右子树进行右单旋
RotateR(ptr->_pRight);
//对根为ptr的树进行左单旋
RotateL(ptr);
//更新平衡因子
if (-1 == bf){
ptr->_bf = 1;
}
else if (1 == bf){
pSubR->_bf = -1;
}
else
{
;
}
}
//判断AVL树是否平衡
bool _IsBalance(pNode pRoot, int& depth){
if (NULL == pRoot){
depth = 0;
return true;
}
int LeftDepth = 0;
int RightDepth = 0;
if (false == _IsBalance(pRoot->_pLeft, LeftDepth))
return false;
if (false == _IsBalance(pRoot->_pRight, RightDepth))
return false;
if (LeftDepth - RightDepth != pRoot->_bf){
cout << "<" << pRoot->_key << "," << pRoot->_value << "> " << "bf异常:" << pRoot->_bf << endl;
return false;
}
depth = LeftDepth > RightDepth ? (LeftDepth + 1) : (RightDepth + 1);
return true;
}
//求取树的深度
int _Depth(pNode pRoot){
if (NULL == pRoot)
return 0;
int LeftDepth = 0;
int RightDepth = 0;
LeftDepth = _Depth(pRoot->_pLeft);
RightDepth = _Depth(pRoot->_pRight);
return LeftDepth > RightDepth ? (LeftDepth + 1) : (RightDepth + 1);
}
private:
pNode _pRoot;
};
test.cpp
#include<Windows.h>
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include"AVLTree.h"
using namespace std;
//测试AVL树
void test3(){
//普通场景
int arr1[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
AVLTree<int, int> tree1;
for (int i = 0; i < sizeof(arr1) / sizeof(arr1[0]); ++i){
tree1.Insert(arr1[i], i);
}
//tree1是否为二叉搜索树,二叉搜索树的中序遍历是有序的
tree1.InOder();
//tree1是否是平衡的
if (tree1.IsBalance())
cout << "普通场景:tree1是平衡的" << endl;
else
cout << "普通场景:tree1不平衡" << endl;
//特殊场景
int arr2[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
AVLTree<int, int> tree2;
for (int i = 0; i < sizeof(arr2) / sizeof(arr2[0]); ++i){
tree2.Insert(arr2[i], i);
}
//tree2是否为二叉搜索树,二叉搜索树的中序遍历是有序的
tree2.InOder();
//tree2是否是平衡的
if (tree2.IsBalance())
cout << "特殊场景:tree2是平衡的" << endl;
else
cout << "特殊场景:tree2不平衡" << endl;
}
int main(){
test3();
system("pause");
return 0;
}
程序运行结果: