二叉搜索树虽然可以提高查找效率,但是在数据有序或近似有序的情况下,二叉搜索树会退化为单枝树,查找效率大大降低,1962年两位俄罗斯数学家联合发明了以一种解决问题的办法,在对二叉搜索树进行插入时,保证每个节点左右子树高度之差的绝对值不超过1,那么就可以降低树的高度,进而减少平均搜索的深度。这种结构的二叉搜索树就是AVL树。
AVL树实现
节点的创建:
节点中需要的成员有,左右孩子指针,增加了一个双亲的指针,表示平衡因子的bf,以及值域data。
template<class T>
struct AVLTreeNode {
AVLTreeNode* left;
AVLTreeNode* right;
AVLTreeNode* parent;
int bf;
T data;
AVLTreeNode(const T& x)
:left(nullptr)
,right(nullptr)
,parent(nullptr)
,bf(0)
,data(x)
{}
};
树的构造:
template<class T>
class AVLTree {
typedef AVLTreeNode<T> Node;
public:
AVLTree()
:root(nullptr)
{}
~AVLTree() {
Destory(root);
}
插入方法:
AVL树的插入与二叉搜索树插入过程类似,首先需要找待插入元素的位置,然后插入元素,到这里是相同的步骤,接下来要满足AVL树的特性,插入新节点cur之后需要更新节点的parent的平衡因子,平衡因子更新有这些情况,
1.新插入的节点在parent的左子树中,这种情况下以parent为根的二叉树,左子树的高度增加了1,所以parent的平衡因子要减去1,
2.新插入的节点在parent的右子树中,那么以parent为根的二叉树右子树的高度增加了1,所以parent的平衡因子要加上1
在parent的平衡因子更新后,要看parent平衡因子的值,判断是否更新完成
1.更新后parent的平衡因子是0,说明以parent为根的这颗二叉树在插入前后整体高度没有改变,所以对上层的平衡不会产生影响
如
2.更新后parent的平衡因子是-1或1,说明以parent为根的树高度发生变化,对上层的平衡有影响,需要继续向上更新
3.当parent更新到一个节点,平衡因子为2或者-2的时候,说明AVL树的结构已经被破坏,此时需要通过名为旋转的方法来重新调整AVL树的结构,根据新插入节点位置的不同,旋转需要分为4种情况
①右单旋
在新节点插入后,cur和parent更新到图中位置时,parent的平衡因子更新后为-2,违反了AVL树的性质,而以parent为根的二叉树左子树高度增加了,因此右单旋的实质就是让左子树高度减少1,让右子树高度增加1.
我们给在旋转过程中连接方式发生变化的几个节点加上标识
结果:
②左单旋:
以parent为根的二叉树右子树高度增加了,通过旋转把右子树高度降低1,让左子树高度增加1.
结果
左右双旋转,先左后右:
先对虚线框部分左单旋,
然后整体进行右单旋:
双旋,先右后左
先右:
整体左单旋
总结一下:需要直接进行右单旋的时候,新插入节点插入到了较高左子树的外侧,
此时parent的bf为-2,说明是左子树高度增加了1,cur,即parent的左孩子,bf为-1,
需要进行左单旋的时候,新插入节点在较高右子树的外侧,parent的bf为2,cur的bf为1
需要进行双选且是先左单旋后右单旋的时候,
新插入的节点位于较高左子树的内侧,
需要进行双旋且先右单旋再左单旋的时候,
AVL树及插入
#pragma once
#include <iostream>
using namespace std;
template<class T>
struct AVLTreeNode{
AVLTreeNode<T>* left;
AVLTreeNode<T>* right;
AVLTreeNode<T>* parent;
T data;
int bf; // 表示节点的平衡因子
AVLTreeNode(const T& x)
: left(nullptr)
, right(nullptr)
, parent(nullptr)
, data(x)
, bf(0)
{}
};
template<class T>
class AVLTree{
typedef AVLTreeNode<T> Node;
public:
AVLTree()
: root(nullptr)
{}
~AVLTree(){
Destroy(root);
}
bool Insert(const T& val){
if (nullptr == root){
root = new Node(val);
return true;
}
Node* cur = root;
Node* parent = nullptr;
while (cur) {
parent = cur;
if (val < cur->data) {
cur = cur->left;
}
else if (val > cur->data) {
cur = cur->right;
}
else {
return false;
}
}
cur = new Node(val);
if (val < parent->data) {
parent->left = cur;
}
else {
parent->right = cur;
}
cur->parent = parent;
//更新parent的平衡因子
while (parent){
if (cur == parent->left) {
parent->bf--;
}
else {
parent->bf++;
}
if (0 == parent->bf) { // 以parent为根的二叉树的高度没有改变,对上层不会有任何影响
break;
}
else if (1 == parent->bf || -1 == parent->bf){
// 以parent为根的二叉树的高度增加了,需要继续往上更新
cur = parent;
parent = cur->parent;
}
else{
// parent节点已经违反AVL树的特性
// 需要对以parent为根的二叉树进行旋转处理
if (-2 == parent->bf) {
// parent的左子树高---最终需要右单旋
if (-1 == cur->bf) {
RotateRight(parent);
}
else {
RotateLR(parent);
}
}
else {
// parent的右子树高---最终需要左单旋
if (1 == cur->bf) {
RotateLeft(parent);
}
else {
RotateRL(parent);
}
}
break;
}
}
return true;
}
void InOrder(){
_InOrder(root);
cout << endl;
}
bool IsAVLTree(){
return _IsAVLTree(root);
}
private:
bool _IsAVLTree(Node* proot){
if (nullptr == proot){
return true;
}
int leftHeight = _Height(proot->left);
int rightHeight = _Height(proot->right);
int bf = rightHeight - leftHeight;
if (abs(proot->bf) > 1 || bf != proot->bf){
cout << proot->data << ":" << bf << "--" << proot->bf << endl;
return false;
}
return _IsAVLTree(proot->left) && _IsAVLTree(proot->right);
}
int _Height(Node* proot){
if (nullptr == proot) {
return 0;
}
int leftHeight = _Height(proot->left);
int rightHeight = _Height(proot->right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
void _InOrder(Node* proot){
if (proot){
_InOrder(proot->left);
cout << proot->data << " ";
_InOrder(proot->right);
}
}
void RotateRight(Node* parent){
Node* subL = parent->left;
Node* subLR = subL->right;
parent->left = subLR;
if (subLR) {
subLR->parent = parent;
}
subL->right = parent;
Node* pparent = parent->parent;
parent->parent = subL;
subL->parent = pparent;
if (nullptr == pparent){
root = subL;
}
else {
if (parent == pparent->left) {
pparent->left = subL;
}
else {
pparent->right = subL;
}
}
subL->bf = parent->bf = 0;
}
void RotateLeft(Node* parent){
Node* subR = parent->right;
Node* subRL = subR->left;
parent->right = subRL;
if (subRL) {
subRL->parent = parent;
}
subR->left = parent;
Node* pparent = parent->parent;
parent->parent = subR;
subR->parent = pparent;
if (nullptr == pparent){
root = subR;
}
else {
if (parent == pparent->left) {
pparent->left = subR;
}
else {
pparent->right = subR;
}
}
parent->bf = subR->bf = 0;
}
void RotateLR(Node* parent){
Node* subL = parent->left;
Node* subLR = subL->right;
int bf = subLR->bf;
RotateLeft(parent->left);
RotateRight(parent);
// 重新更新部分节点的平衡因子
if (-1 == bf) {
parent->bf = 1;
}
else if (1 == bf) {
subL->bf = -1;
}
}
void RotateRL(Node* parent) {
Node* subR = parent->right;
Node* subRL = subR->left;
int bf = subRL->bf;
RotateRight(parent->right);
RotateLeft(parent);
// 重新更新部分节点的平衡因子
if (-1 == bf) {
subR->bf = 1;
}
else if (1 == bf) {
parent->bf = -1;
}
}
void Destroy(Node*& proot){
if (proot){
Destroy(proot->left);
Destroy(proot->right);
delete proot;
proot = nullptr;
}
}
private:
Node* root;
};
应用
AVL树的优点在于高度平衡,因此查找效率很高,但是其在插入和删除的过程中有时候需要大量的进行旋转,尤其在删除时,最差的情况下要一直旋转直到更新到根的位置。一般需要用到平衡树的场景中很少会考虑到AVL树,更多的会使用红黑树。