一、AVL的说明
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右 子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均 搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
1、它的左右子树都是AVL树
2、左右子树高度之差(简称平衡因子)的绝对值不超过1(-10/1)
3、如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 $O(log_2 n)$,搜索时间复杂度O($log_2 n$)。
二、AVL树实现细节
1、AVL树节点的定义
template<class T>
struct AVLTreeNode
{
AVLTreeNode(const T& data)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _bf(0)
{}
AVLTreeNode<T>* _pLeft; // 该节点的左孩子
AVLTreeNode<T>* _pRight; // 该节点的右孩子
AVLTreeNode<T>* _pParent; // 该节点的双亲
T _data;
int _bf; // 该节点的平衡因子
};
2、AVL树的insert
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么 AVL树的插入过程可以分为两步:
1. 按照二叉搜索树的方式插入新节点
bool insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->left;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->right = cur;
cur->parent = parent;
}
else
{
parent->left = cur;
cur->parent = parent;
}
//调节平衡因子
....
return true;
}
2. 调整节点的平衡因子
如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构, 使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:
1. 新节点插入较高左子树的左侧---左左:右单旋
实现代码
//右旋
void RotateR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
subL->right = parent;
parent->left = subLR;
Node* parentParent = parent->parent;
parent->parent = subL;
if (subLR)
subLR->parent = parent;
//如果父亲的根节点
if (parent == _root)
{
_root = subL;
subL->parent = nullptr;
}
else
{
if (parentParent->left == parent)
{
parentParent->left = subL;
}
else
{
parentParent->right = subL;
}
subL->parent = parentParent;
}
subL->_bf = parent->_bf = 0;
}
2. 新节点插入较高右子树的右侧---右右:左单旋
实现代码
void RotateL(Node*parent)
{
Node* subR = parent->right;
Node* subRL = subR->left;
subR->left = parent;
parent->right = subRL;
Node* parentParent = parent->parent;
parent->parent = subR;
if (subRL)
subRL->parent = parent;
if (parent == _root)
{
_root = subR;
subR->parent = nullptr;
}
else
{
if (parentParent->left == parent)
{
parentParent->left = subR;
}
else
{
parentParent->right = subR;
}
subR->parent = parentParent;
}
parent->_bf = subR->_bf = 0;
}
3. 新节点插入较高左子树的右侧---左右:先左单旋再右单旋
实现代码
void RotateLR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
if (bf == 1)
{
subL->_bf = -1;
parent->_bf = subLR->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = subLR->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = subL->_bf = subLR->_bf = 0;
}
else
{
assert(false);
}
}
4. 新节点插入较高右子树的左侧---右左:先右单旋再左单旋
实现代码
void RotateRL(Node* parent)
{
Node* subR = parent->right;
Node* subRL = subR->left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(parent);
if (bf == 1)
{
parent->_bf = -1;
subR->_bf = subRL->_bf = 0;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = subRL->_bf = 0;
}
else if(bf==0)
{
subR->_bf = parent->_bf = subRL->_bf = 0;
}
else
{
assert(false);
}
}
总结:
假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑
1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR
当pSubR的平衡因子为1时,执行左单旋
当pSubR的平衡因子为-1时,执行右左双旋
2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL
当pSubL的平衡因子为-1是,执行右单旋
当pSubL的平衡因子为1时,执行左右双旋 旋转完成后,
原pParent为根的子树个高度降低,已经平衡,不需要再向上更新
三、验证AVL树
AVL树的验证 AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:
1. 验证其为二叉搜索树
如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
2. 验证其为平衡树
每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子) 节点的平衡因子是否计算正确
代码实现
bool IsBalance()
{
return _IsBalance(_root);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftheight = _Height(root->left);
int rightheight = _Height(root->right);
return leftheight < rightheight ? rightheight + 1 : leftheight + 1;
}
bool _IsBalance(Node* root)
{
if (root == nullptr)
{
return true;
}
int leftheight = _Height(root->left);
int rightheight = _Height(root->right);
if (rightheight - leftheight != root->_bf)
{
cout << root->_kv.first << ":平衡因子异常" << endl;
}
return abs(rightheight - leftheight) < 2
&& _IsBalance(root->left)
&& _IsBalance(root->right);
}
3. 验证用例 结合上述代码按照以下的数据次序,自己动手画AVL树的创建过程,验证代码 是否有漏洞。
常规场景1 {16, 3, 7, 11, 9, 26, 18, 14, 15}
特殊场景2 {4, 2, 6, 1, 3, 5, 15, 7, 16, 14}
四、完整代码
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode* left;
AVLTreeNode* right;
AVLTreeNode* parent;
pair<K, V> _kv;
int _bf;
AVLTreeNode(const pair<K, V>& kv)
:left(nullptr)
, right(nullptr)
, parent(nullptr)
, _kv(kv)
, _bf(0)
{}
};
template<class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
bool insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->left;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->right = cur;
cur->parent = parent;
}
else
{
parent->left = cur;
cur->parent = parent;
}
while (parent)
{
if (parent->left == cur)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
//parent向上找parent->bf,和孩子符合以下要求的平衡因子再进行处理
parent = parent->parent;
}
else if(parent->_bf==2||parent->_bf==-2)
{
if (parent->_bf == 2 && parent->right->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == -2 && parent->left->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == 2 && parent->right->_bf == -1)
{
RotateRL(parent);
}
else if (parent->_bf == -2 && parent->left->_bf== 1)
{
RotateLR(parent);
}
break;
}
else
{
assert(false);
}
}
return true;
}
//左右双旋
void RotateLR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
if (bf == 1)
{
subL->_bf = -1;
parent->_bf = subLR->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = subLR->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = subL->_bf = subLR->_bf = 0;
}
else
{
assert(false);
}
}
//右左双旋
void RotateRL(Node* parent)
{
Node* subR = parent->right;
Node* subRL = subR->left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(parent);
if (bf == 1)
{
parent->_bf = -1;
subR->_bf = subRL->_bf = 0;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = subRL->_bf = 0;
}
else if(bf==0)
{
subR->_bf = parent->_bf = subRL->_bf = 0;
}
else
{
assert(false);
}
}
//左旋
void RotateL(Node*parent)
{
Node* subR = parent->right;
Node* subRL = subR->left;
subR->left = parent;
parent->right = subRL;
Node* parentParent = parent->parent;
parent->parent = subR;
if (subRL)
subRL->parent = parent;
if (parent == _root)
{
_root = subR;
subR->parent = nullptr;
}
else
{
if (parentParent->left == parent)
{
parentParent->left = subR;
}
else
{
parentParent->right = subR;
}
subR->parent = parentParent;
}
parent->_bf = subR->_bf = 0;
}
//右旋
void RotateR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
subL->right = parent;
parent->left = subLR;
Node* parentParent = parent->parent;
parent->parent = subL;
if (subLR)
subLR->parent = parent;
//如果父亲的根节点
if (parent == _root)
{
_root = subL;
subL->parent = nullptr;
}
else
{
if (parentParent->left == parent)
{
parentParent->left = subL;
}
else
{
parentParent->right = subL;
}
subL->parent = parentParent;
}
subL->_bf = parent->_bf = 0;
}
void Inorder()
{
_Inorder(_root);
}
bool IsBalance()
{
return _IsBalance(_root);
}
private:
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftheight = _Height(root->left);
int rightheight = _Height(root->right);
return leftheight < rightheight ? rightheight + 1 : leftheight + 1;
}
bool _IsBalance(Node* root)
{
if (root == nullptr)
{
return true;
}
int leftheight = _Height(root->left);
int rightheight = _Height(root->right);
if (rightheight - leftheight != root->_bf)
{
cout << root->_kv.first << ":平衡因子异常" << endl;
}
return abs(rightheight - leftheight) < 2
&& _IsBalance(root->left)
&& _IsBalance(root->right);
}
void _Inorder(Node* root)
{
if (root == nullptr)
return;
_Inorder(root->left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_Inorder(root->right);
}
Node* _root=nullptr;
};
一般同学了解即可。。。