继二叉搜索树后的AVL树
为解决二叉搜索树退化为单支树问题,在二叉搜索树上增加限制条件,即AVL树和红黑树,其中红黑树又是map/set关联式容器实现的底层结构。
文章目录
前言
在map/multimap/set/multiset文档介绍中我们可以知道这几个容器有个共同点为:其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,若往树中插入的元素有序或者接近有序,则二叉搜索树便会退化为单支树,其时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。
编译环境:VS2013
一、AVL树的概念
一棵AVL树:二叉搜索树+限制条件(其左右子树高度差绝对值不大于1)
1.空树
2.非空时,满足:
a.左右子树都是AVL树
b.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在
O(log_2 n),搜索时间复杂度O(log_2 n)。
/AVL树节点定义
template<class T>
class AVLTreeNode
{
AVLTreeNode<T>* _left;//节点左孩子
AVLTreeNode<T>* _right;//节点右孩子
AVLTreeNode<T>* _parent;//节点双亲
T _data; //节点值域
int _bf; //该节点的平衡因子
AVLTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _bf(0)
{}
};
二、AVL树的插入
树的左右子树高度差绝对值不大于1的二叉搜索树即为AVL树,故AVL树的节点插入第一步同二叉搜索树的节点插入,元素插入后原树的平衡因子可能遭到破坏,故我们需对树进行调整。
step1:按照二叉搜索树的方式进行节点的插入。
step2:调整节点的平衡因子。
而对树进行调整的过程,即对树进行旋转来调整其平衡因子。
//AVL树节点插入
//1.按照二叉搜索树的方式插入新节点
//2.调整节点的平衡因子(平衡因子规定为:右子树高度-左子树高度)
bool Insert(const T& data)
{
if (_root == nullptr)
{
//新插入节点即为根节点
_root = new Node(data);
return true;
}
//1.按照二叉搜索树插入新节点方式往AVL树中插入新节点
//a.查找带插入节点在树中是否存在
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
parent = cur;
if (data < cur->_data)
cur = cur->_left;
else if (data>cur->_data)
cur =cur->_right;
else
return false;
}
//b.走下来表示未找到,进行节点的插入
cur = new Node(data);
if (data < parent->_data)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//节点插入成功后,其parent的子树高度改变,故需更新parent的平衡因子
while (parent)
{
if (cur == parent->_left)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
/
if (parent->_bf == 0)//说明节点在插入之前parent有一孩子,插入新节点并未影响整体的树的高度,只是影响parent的平衡因子
break;
else if (1==parent->_bf || -1==parent->_bf )
{
//说明以parent为根的二叉树高度增加了一层,需要继续往上更新平衡因子
cur = parent;
parent = cur->_parent;
}
else //parent的平衡因子为2或-2
{
if (parent->_bf == 2)
{
//说明其右子树高
if (cur->_bf == 1)
{
RotateLeft(parent);//右子树高且为较高右子树的右侧,故左单旋
}
else
{
//右子树较高且为较高右子树的左侧,故子树先右单旋再整体左单旋,即右左双旋
RotateRL(parent);
//等价于:先RotateRight(parent->_right)再RotateLeft(parent)
}
}
else
{
//说明其左子树高
if (cur->_bf == -1)
{
RotateRight(parent);//左子树较高且为较高左子树的左侧,故右单旋
}
else
{
//左子树较高且为较高左子树的右侧,故子树先左单旋再整体右单旋,即左右双旋
RotateLR(parent);
//等价于:先RotateLeft(parent->_left)再RotateRight(parent)
}
}
//在元素插入之前以parent为根的二叉树高度
//插入元素后进行树的旋转之后以parent为根的二叉树的高度一样
//故无需继续向上更新
break;
}
}
return true;
}
三、AVL树的旋转
如果在一棵原本是平衡的AVL树中插入一个新节点后,可能破坏原树的平衡,此时必须调整树的结构,使之平衡化。而根据节点插入位置的不同,我们将AVL树的旋转分为四种:右单旋、左单旋、右左双旋(先右单旋再左单旋)、左右双旋(先左单旋再右单旋)。
而对于每种旋转都有其对应的场景,具体如下。
1.右单旋
场景:当新节点插入到较高左子树的左侧时。
首先,我们先来分别一下较高左子树和较高子树的左侧和右侧概念。
由上图我们可以看到,往树中插入元素3后,根结点的平衡因子为-2,导致树不平衡。
原因为:插入元素3后,元素4的左子树高度增加1,由此上推,元素5和7的左子树也都增加了1,左子树高度增加,树失衡。此场景下旋转调整主要思想即将左子树高度降低,右子树高度升高,以达到树的平衡。
当插入元素在元素4的右侧时,树的调整同插入元素3时。
综上,我们可以看到,在向上述AVL树中插入元素3和元素4.5的处理方法一致。
注意:此处元素7为根节点,而节点7也可能为某一节点的左孩子或是右孩子 ,即元素7有双亲。若节点7有双亲,在右单旋修改元素指针指向时,节点7双亲的对应指针也应修改为指向节点5。
//右单旋
void RotateRight(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
//进行①指针的指向修改
parent->_left = subLR;
//subLR可能为空
if (subLR)
{
subLR->_parent = parent;
}
//进行②指针的指向修改
subL->_right = parent;
Node* pParent = parent->_parent;
parent->_parent = subL;
subL->_parent = pParent;
//parent为根节点时,subR直接为新的根节点
//parent不为根节点时,parent可能为pParent的左孩子或是右孩子
if (pParent == nullptr)
{
_root = subL;
}
else
{
if (parent == pParent->_left)
{
pParent->_left = subL;
}
else
{
pParent->_right = subL;
}
}
//将平衡因子进行订正更新
parent->_bf = subL -> _bf = 0;
}
2.左单旋
场景:当新节点插入到较高右子树的右侧时。
综上,我们可以看到,在向上述AVL树中插入元素7和元素5.5的处理方法一致。
注意:同右单旋,此处元素3为根节点,而节点3也可能为某一节点的左孩子或是右孩子 ,即元素3有双亲。若节点3有双亲,在左单旋修改元素指针指向时,节点3双亲的指针对应也应修改为指向节点5。
//左单旋
void RotateLeft(Node* parent)
{
Node*subR = parent->_right;
Node* subRL = subR->_left;
//进行①指针的指向修改
parent->_right = subRL;
//subRL可能为空
if (subRL)
{
subRL->_parent = parent;
}
//进行②指针的指向修改
subR->_left = parent;
Node* pParent = parent->_parent;
parent->_parent = subR;
subR->_parent = pParent;
//parent为根节点时,subR直接为新的根节点
//parent不为根节点时,parent可能为pParent的左孩子或是右孩子
if (pParent == nullptr)
{
_root = subR;
}
else
{
if (parent == pParent->_left)
{
pParent ->_left = subR;
}
else
{
pParent->_right = subR;
}
}
//将平衡因子进行订正更新
parent->_bf = subR->_bf = 0;
}
3.左右双旋
场景:当新节点插入到较高左子树的右侧时。
综上,我们可以看到,在向上述AVL树中插入元素5.5和元素6.5的处理方法一致。
//左右双旋
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateLeft(parent->_left);
RotateRight(parent);
//双旋后进行平衡因子的更新
if (bf == 1)
{
subL->_bf = -1;
}
else if (bf == -1)
{
parent->_bf = 1;
}
else
;//bf为0的时候不需要更新
}
4.右左双旋
场景:当新节点插入到较高右子树的左侧时。
综上,我们可以看到,在向上述AVL树中插入元素3.5和元素4.5的处理方法一致。
//右左双旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateRight(parent->_right);
RotateLeft(parent);
if (bf == 1)
parent->_bf = -1;
else if (bf==-1)
subR->_bf = 1;
else
;
}
四、AVL树的检测
1.检测思路
因为AVL树是在二叉搜索树的基础上加入了平衡性的限制条件,因此要验证AVL树,我们可以先验证其是否为二叉搜索树,再验证其节点的平衡因子。
step1:验证其为二叉搜索树
如果中序遍历可得到一个有序的序列,就说明其为二叉搜索树。
step2:验证其为平衡树
a.每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)。
b.节点的平衡因子是否计算正确。
2.实现
bool _IsValidAVLTree(Node* root)
{
if (root == nullptr)
return true;
//树非空,用平衡因子计算公式进行树的平衡因子的检测
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
if (abs(rightHeight - leftHeight) > 1 || ((rightHeight - leftHeight) != root->_bf))
{
cout << root->_data << " " << root->_bf << " " << rightHeight - leftHeight << endl;
//节点处的平衡因子出错
//前者为创建的树更新的平衡因子
//后者根据平衡因子计算公式进行数据计算
return false;
}
return _IsValidAVLTree(root->_left) && _IsValidAVLTree(root->_right);
}
五、AVL树的性能
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即log_2 (N)。
但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。
六、测试结果展示及完整代码
//测试函数//
void TestAVLTree()
{
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
AVLTree<int> t;
for (auto e : a)
{
t.Insert(e);
}
t.Inorder();
if (t.IsValidAVLTree())
{
cout << endl;
cout << "t is valid AVLTree" << endl;
}
else
{
cout << endl;
cout << "t is invalid AVLTree" << endl;
}
}
AVL.hpp
#pragma once
template<class T>
class AVLTreeNode
{
public:
AVLTreeNode<T>* _left;//节点左孩子
AVLTreeNode<T>* _right;//节点右孩子
AVLTreeNode<T>* _parent;//节点双亲
T _data; //节点值域
int _bf; //该节点的平衡因子
AVLTreeNode(const T& data=T())
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _bf(0)
{}
};
//规定:树中元素唯一
template<class T>
class AVLTree
{
typedef AVLTreeNode<T> Node;
public:
AVLTree()
:_root(nullptr)
{}
~AVLTree()
{
_Destroy(_root);
}
//AVL树节点插入
//1.按照二叉搜索树的方式插入新节点
//2.调整节点的平衡因子(平衡因子规定为:右子树高度-左子树高度)
bool Insert(const T& data)
{
if (_root == nullptr)
{
//新插入节点即为根节点
_root = new Node(data);
return true;
}
//1.按照二叉搜索树插入新节点方式往AVL树中插入新节点
//a.查找带插入节点在树中是否存在
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
parent = cur;
if (data < cur->_data)
cur = cur->_left;
else if (data>cur->_data)
cur =cur->_right;
else
return false;
}
//b.走下来表示未找到,进行节点的插入
cur = new Node(data);
if (data < parent->_data)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//节点插入成功后,其parent的子树高度改变,故需更新parent的平衡因子
while (parent)
{
if (cur == parent->_left)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
/
if (parent->_bf == 0)//说明节点在插入之前parent有一孩子,插入新节点并未影响整体的树的高度,只是影响parent的平衡因子
break;
else if (1==parent->_bf || -1==parent->_bf )
{
//说明以parent为根的二叉树高度增加了一层,需要继续往上更新平衡因子
cur = parent;
parent = cur->_parent;
}
else //parent的平衡因子为2或-2
{
if (parent->_bf == 2)
{
//说明其右子树高
if (cur->_bf == 1)
{
RotateLeft(parent);//右子树高且为较高右子树的右侧,故左单旋
}
else
{
//右子树较高且为较高右子树的左侧,故子树先右单旋再整体左单旋,即右左双旋
RotateRL(parent);
//等价于:先RotateRight(parent->_right)再RotateLeft(parent)
}
}
else
{
//说明其左子树高
if (cur->_bf == -1)
{
RotateRight(parent);//左子树较高且为较高左子树的左侧,故右单旋
}
else
{
//左子树较高且为较高左子树的右侧,故子树先左单旋再整体右单旋,即左右双旋
RotateLR(parent);
//等价于:先RotateLeft(parent->_left)再RotateRight(parent)
}
}
//在元素插入之前以parent为根的二叉树高度
//插入元素后进行树的旋转之后以parent为根的二叉树的高度一样
//故无需继续向上更新
break;
}
}
return true;
}
void Inorder()
{
cout << "树的中序遍历为:" << endl;
_Inorder(_root);
}
//检测构造出来的树是否是AVL树
bool IsValidAVLTree()
{
return _IsValidAVLTree(_root);
}
private:
void _DestroyAVLTree(Node*& root)
{
if (root)
{
_DestroyAVLTree(root->_left);
_DestroyAVLTree(root->_right);
delete root;
root = nullptr;
}
}
void _Inorder(Node* root)
{
if (root)
{
_Inorder(root->_left);
cout << root->_data << " ";
_Inorder(root->_right);
}
}
//求树的高度
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _IsValidAVLTree(Node* root)
{
if (root == nullptr)
return true;
//树非空,用平衡因子计算公式进行树的平衡因子的检测
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
if (abs(rightHeight - leftHeight) > 1 || ((rightHeight - leftHeight) != root->_bf))
{
cout << root->_data << " " << root->_bf << " " << rightHeight - leftHeight << endl;
//节点处的平衡因子出错,前者为创建的树更新的平衡因子,后者根据平衡因子计算公式进行数据计算
return false;
}
return _IsValidAVLTree(root->_left) && _IsValidAVLTree(root->_right);
}
//左单旋
void RotateLeft(Node* parent)
{
Node*subR = parent->_right;
Node* subRL = subR->_left;
//进行①指针的指向修改
parent->_right = subRL;
//subRL可能为空
if (subRL)
{
subRL->_parent = parent;
}
//进行②指针的指向修改
subR->_left = parent;
Node* pParent = parent->_parent;
parent->_parent = subR;
subR->_parent = pParent;
//parent为根节点时,subR直接为新的根节点
//parent不为根节点时,parent可能为pParent的左孩子或是右孩子
if (pParent == nullptr)
{
_root = subR;
}
else
{
if (parent == pParent->_left)
{
pParent ->_left = subR;
}
else
{
pParent->_right = subR;
}
}
//将平衡因子进行订正更新
parent->_bf = subR->_bf = 0;
}
//右单旋
void RotateRight(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
//进行①指针的指向修改
parent->_left = subLR;
//subLR可能为空
if (subLR)
{
subLR->_parent = parent;
}
//进行②指针的指向修改
subL->_right = parent;
Node* pParent = parent->_parent;
parent->_parent = subL;
subL->_parent = pParent;
//parent为根节点时,subR直接为新的根节点
//parent不为根节点时,parent可能为pParent的左孩子或是右孩子
if (pParent == nullptr)
{
_root = subL;
}
else
{
if (parent == pParent->_left)
{
pParent->_left = subL;
}
else
{
pParent->_right = subL;
}
}
//将平衡因子进行订正更新
parent->_bf = subL -> _bf = 0;
}
//左右双旋
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateLeft(parent->_left);
RotateRight(parent);
//双旋后进行平衡因子的更新
if (bf == 1)
{
subL->_bf = -1;
}
else if (bf == -1)
{
parent->_bf = 1;
}
else
;//bf为0的时候不需要更新
}
//右左双旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateRight(parent->_right);
RotateLeft(parent);
if (bf == 1)
parent->_bf = -1;
else if (bf==-1)
subR->_bf = 1;
else
;
}
private:
AVLTreeNode<T>* _root;
};
void TestAVLTree()
{
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
AVLTree<int> t;
for (auto e : a)
{
t.Insert(e);
}
t.Inorder();
if (t.IsValidAVLTree())
{
cout << endl;
cout << "t is valid AVLTree" << endl;
}
else
{
cout << endl;
cout << "t is invalid AVLTree" << endl;
}
}
AVL.cpp
#include<iostream>
using namespace std;
#include"AVL.hpp"
int main()
{
TestAVLTree();
system("pause");
return 0;
}