目录
AVL树的概念及结构
概念
对于普通二叉搜索树,如果在数据有序或者接近有序时插入删除,二叉搜索树会退化成单支树,也就是类似单链表的结构;因此为了避免普通二叉搜索树的高度增长过快,降低二叉搜索树的效率与性能。
规定在插入和删除二叉搜索树的结点时,要保证任意结点的左右子树高度差的绝对值不超过1,我们将这样特殊的二叉搜索树称为AVL树(平衡二叉树)。每一个结点中有记录其左右子树高度差的平衡因子,也即平衡因子的值只可能是-1,0,1。(1如果某结点的左子树高度比右子树高度高一则平衡因子为-1,如果某结点的左子树高度比右子树高度低一则平衡因子为1,如果某结点的左子树高度与右子树高度一样则平衡因子为0)。
AVL树有以下性质:
1,每个结点的左右子树都是AVL树
2,左子树与右子树的高度差只能为-1,0,1(规定平衡因子等于右子树的高度减左子树的高度)
3,如果AVL树非常平衡,则整体结构接近满二叉树,所以当结点为N个时,其高度为log(2)N
结构
为了便于更新平衡因子与旋转处理AVL树的链式结构采用三叉链,也就是AVL树的每个结点都包含:双亲结点地址,左孩子地址,右孩子地址,平衡因子,KV数据。
AVL树的插入
第一步:按照二叉搜索树的插入操作进行新结点的插入,但需要注意的是要将新插入结点中的_parent指向改为其双亲。
第二步:更新平衡因子,从插入结点的双亲开始往上更新;如果插入的结点是双亲的右孩子则双亲的平衡因子加一,如果插入的结点是双亲的左孩子则双亲的平衡因子减一;更新过的平衡因子有三种情况:
1,如果平衡因子变为0,则说明原来的平衡因子为-1或者1,也就是原来左右子树的高度差绝对值为1,经过插入新结点,使该结点的左右子树达到平衡,所以无须继续往上更新平衡因子。
2,如果平衡因子变为-1或者1,则说明原来的平衡因子为0,也就是原来左右子树的高度一致,经过插入新结点,使该结点的左右子树的高度差绝对值变为1,所以需要继续往上更新平衡因子。
3,如果平衡因子变为-2或者2,则说明原来的平衡因子为-1或者1,也就是原来左右子树高度差绝对值为1,经过插入新结点,使该结点的左右子树高度差绝对值为2,造成不满足AVL树性质要求需要进行旋转处理。
第三步:旋转处理,使不平衡的AVL树变为平衡的AVL树;造成平衡的AVL树变为不平衡的AVL树的原因有4种,也就是说使不平衡的AVL树变为平衡的AVL树的旋转处理方式也有4种,每一种原因对应一种旋转处理方式。
右旋转
在A结点的左孩子B的左子树上插入新结点,导致以A为根的子树失去平衡(左子树比右子树高),需要进行右旋转处理
右旋转的具体操作:先让B结点的_right指向A结点,再让A结点的_left指向原B结点的右子树b。
左旋转
在A结点的右孩子B的右子树上插入新结点,导致以A为根的子树失去平衡(左子树比右子树低),需要进行左旋转处理。
左旋转的具体操作:先让B结点的_left指向A结点,再让A结点的_right指向原B结点的左子树b。
左右双旋
由于在A的左孩子B的右子树上插入新结点,A的平衡因子由-1变为-2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先左旋转再右旋转。左旋转是以B结点为轴,右旋转是以A结点为轴。
具体操作:先将A结点的左孩子B的右子树的根节点C向左上旋转提升到B结点的位置,然后把该C结点向右上旋转提升到A结点的位置。
// 左右旋
void RotateLR(Node* parrnt)
{
Node* subL = parrnt->_left;
Node* subLR = subL->_right;
short bf = subLR->_bf;
RotateL(parrnt->_left);
RotateR(parrnt);
if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
parrnt->_bf = 0;
}
else if (bf == -1)
{
subLR->_bf = 0;
subL->_bf = 0;
parrnt->_bf = 1;
}
else if(bf==0)
{
subLR->_bf = 0;
subL->_bf = 0;
parrnt->_bf = 0;
}
else
{
assert(false);
}
}
右左旋转
由于在A的右孩子B的左子树上插入新结点,A的平衡因子由1变为2,导致以A为根的子树失去平衡,需要进行两次旋转操作,先右旋转在左旋转。右旋转是以B结点为轴,左旋转是以A结点为轴。
具体操作:先将A结点的右孩子B的左子树的根节点C向右上旋转提升到B结点的位置,然后把该C结点向左上旋转提升到A结点的位置。
// 右左旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
short bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf==-1)
{
subRL->_bf = 0;
subR->_bf = 1;
parent->_bf = 0;
}
else if(bf==0)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
AVL树的删除
第一步:先找到要删除的结点
第二步:按照要删除结点的类型进行分类删除:1,左子树为空 2,右子树为空 3,左右子树都为空 4,左右子树都不为空
第三步:更新平衡因子。如果删除的结点在其双亲的左边则双亲的平衡因子++,如果删除的结点在其双亲结点的右边则双亲的平衡因子--。
1,如果更新后parent->_bf==0,说明parent->_bf原来是-1或者1,把高的那边删除掉了,高度变小了,继续往上更新。
2,如果更新后parent->_bf==-1或者parenr->_bf==1,说明parent->_bf原来是0,删除了其左右结点其中的一个,高度没有变,不需要继续往上更新。
3,如果更新后parent->_bf==-2或者parenr->_bf==2,说明AVL树不平衡,需要旋转处理。
第四步:旋转处理。
// 旋转规则(有四种情况):
// firstDepth是parent高度最高的孩子结点,secondDepth是firstDepth高度最后的结点
// 1,如果first是parent的左孩子,second是first的左孩子---右旋转
// 2,如果first是parent的左孩子,second是first的右孩子---左右双旋// 3,如果first是parent的右孩子,second是first的右孩子---左旋转
// 4,如果first是parent的右孩子,second是first的左孩子---右左双旋
插入与删除的总结
AVL树的基本操作
构造
// 无参构造
AVLTree()
:_root(nullptr)
{
}
// 拷贝构造
Node* _CopyAVLTree(Node* root, Node* parent)
{
if (root == nullptr)
{
return nullptr;
}
Node* newNode = new Node(root->_kv);
newNode->_bf = root->_bf;
newNode->_parent = parent;
newNode->_left = _CopyAVLTree(root->_left,newNode);
newNode->_right = _CopyAVLTree(root->_right,newNode);
return newNode;
}
AVLTree(const AVLTree& t)
{
_root=_CopyAVLTree(t._root, nullptr);
}
析构
//析构
~AVLTree()
{
_Destroy(_root);
_root = nullptr;
}
//销毁
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
= 重载
AVLTree& operator=(AVLTree t)
{
swap(_root, t._root);
return *this;
}
[ ] 重载
V& operator[](const K& key)
{
pair<Node*, bool> ret = Insert(make_pair(key,V()));
return (ret.first)->_kv.second;
}
中序遍历
void InOrder()
{
_InOrder(_root);
cout << endl;
}
判断一棵树是否为AVL树
bool IsAVLTree()
{
return _IsAVLTree(_root);
}
代码
// .hpp
#pragma once
#include <iostream>
#include <cassert>
using namespace std;
template <class K,class V>
struct AVLTreeNode
{
short _bf; // 平衡因子---记录其左右子树的高度差(右子树高度减左子树高度)
struct AVLTreeNode<K, V>* _parent; // 记录其双亲的地址
struct AVLTreeNode<K, V>* _left; // 记录其左孩子的地址
struct AVLTreeNode<K, V>* _right; // 记录其右孩子的地址
pair<K, V> _kv; // kv值
AVLTreeNode(const pair<K,V>& kv)
:_bf(0)
,_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
,_kv(kv)
{
}
};
template <class K,class V>
class AVLTree
{
typedef struct AVLTreeNode<K, V> Node;
private:
Node* _root;
// 左旋转---左子树低
void RotateL(Node* parent) // parent是平衡因子为-2或者2的结点
{
Node* parentParent = parent->_parent; // 记录其双亲
Node* subR = parent->_right; // 记录其右孩子
Node* subRL = subR->_left; // 记录其右孩子的左孩子
// 让subR的左孩子指向parent,parent的双亲指向subR
subR->_left = parent;
parent->_parent = subR;
// 让parent的右孩子指向subR的左孩子
parent->_right = subRL;
if (subRL) // 如果subRL不为空,则让subR的左孩子的_parent指向parent
{
subRL->_parent = parent;
}
// 更新parent的双亲结点中的左孩子或者右孩子指向,并更新subR的_parent的指向
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else if(parentParent->_right==parent)
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
// 更新旋转处理后的平衡因子
parent->_bf = 0;
subR->_bf = 0;
}
// 右旋转
void RotateR(Node* parent) // parent是平衡因子为-2或者2的结点
{
Node* parentParent = parent->_parent; // 记录其双亲
Node* subL = parent->_left; // 记录其左孩子
Node* subLR = subL->_right; // 记录其左孩子的右孩子
// parent的双亲指向subL,subL的右孩子指向parent
parent->_parent = subL;
subL->_right = parent;
// parent的左孩子指向subL的右孩子
parent->_left = subLR;
if (subLR) // 如果subLR不为空,则让subLR的双亲指向parent
{
subLR->_parent = parent;
}
if (_root == parent) // parent的双亲为空则说明parent是整棵树的根节点
{
_root = subL; // 让subL当整棵树的根节点
subL->_parent = nullptr; // 并让subL的双亲指向空
}
else // 否则parent是某棵子树的根节点,更新parentParent左孩子或者右孩子的指向
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent; // 更新subL双亲的指向
}
parent->_bf = subL->_bf = 0; // 经过旋转更新平衡因子
}
// 左右旋
void RotateLR(Node* parrnt)
{
Node* subL = parrnt->_left;
Node* subLR = subL->_right;
short bf = subLR->_bf;
RotateL(parrnt->_left);
RotateR(parrnt);
if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
parrnt->_bf = 0;
}
else if (bf == -1)
{
subLR->_bf = 0;
subL->_bf = 0;
parrnt->_bf = 1;
}
else if(bf==0)
{
subLR->_bf = 0;
subL->_bf = 0;
parrnt->_bf = 0;
}
else
{
assert(false);
}
}
// 右左旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
short bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf==-1)
{
subRL->_bf = 0;
subR->_bf = 1;
parent->_bf = 0;
}
else if(bf==0)
{
subRL->_bf = 0;
subR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
// parent为删除结点的双亲,child为删除的结点
void UpdateBF(Node* parent,Node* child)
{
while (parent != nullptr)
{
if (parent->_left == child)
{
parent->_bf++;
}
else if (parent->_right == child)
{
parent->_bf--;
}
// 检查平衡因子是否异常
if (parent->_bf == 0)
{
child = parent;
parent = parent->_parent;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
break;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
// 求出平衡因子异常结点的左右子树中高的那一棵子树
Node* firstDepth = parent->_left;
int maxDepth = AVLTreeDepth(parent->_left);
int minDepth = AVLTreeDepth(parent->_right);
if (maxDepth < minDepth)
{
firstDepth = parent->_right;
}
// 求出firstDepth结点的左右子树中高的那一棵子树
Node* secondDepth = firstDepth->_left;
maxDepth = AVLTreeDepth(firstDepth->_left);
minDepth = AVLTreeDepth(firstDepth->_right);
if (maxDepth < minDepth)
{
secondDepth = firstDepth->_right;
}
// 旋转规则(有四种情况):
// firstDepth是parent高度最高的孩子结点,secondDepth是firstDepth高度最后的结点
// 1,如果first是parent的左孩子,second是first的左孩子---右旋转
// 2,如果first是parent的左孩子,second是first的右孩子---左右双旋
// 3,如果first是parent的右孩子,second是first的右孩子---左旋转
// 4,如果first是parent的右孩子,second是first的左孩子---右左双旋
if (parent->_left == firstDepth)
{
if (firstDepth->_left == secondDepth)
{
RotateR(parent);
}
else if (firstDepth->_right == secondDepth)
{
RotateLR(parent);
}
}
else if (parent->_right == firstDepth)
{
if (firstDepth->_left == secondDepth)
{
RotateRL(parent);
}
else if (firstDepth->_right == secondDepth)
{
RotateL(parent);
}
}
break;
}
else
{
assert(false);
}
}
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << " K:" << root->_kv.first << " V:" << root->_kv.second << " bf:" << root->_bf <<endl;
_InOrder(root->_right);
}
// 判断是否为AVL树
bool _IsAVLTree(Node* root)
{
if (root == nullptr)
{
return true;
}
int left = AVLTreeDepth(root->_left);
int right = AVLTreeDepth(root->_right);
return abs(left - right) < 2 && _IsAVLTree(root->_left) && _IsAVLTree(root->_right);
}
// 拷贝构造
Node* _CopyAVLTree(Node* root, Node* parent)
{
if (root == nullptr)
{
return nullptr;
}
Node* newNode = new Node(root->_kv);
newNode->_bf = root->_bf;
newNode->_parent = parent;
newNode->_left = _CopyAVLTree(root->_left,newNode);
newNode->_right = _CopyAVLTree(root->_right,newNode);
return newNode;
}
//销毁
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
public:
AVLTree()
:_root(nullptr)
{
}
AVLTree(const AVLTree& t)
{
_root=_CopyAVLTree(t._root, nullptr);
}
~AVLTree()
{
_Destroy(_root);
_root = nullptr;
}
V& operator[](const K& key)
{
pair<Node*, bool> ret = Insert(make_pair(key,V()));
return (ret.first)->_kv.second;
}
AVLTree& operator=(AVLTree t)
{
swap(_root, t._root);
return *this;
}
pair<Node*,bool> Insert(const pair<K, V>& kv)
{
if (_root == nullptr) // 若AVL为空树,则直接插入
{
_root = new Node(kv);
return make_pair(_root,true);
}
Node* prev = nullptr; // 记录插入结点所在路径的最后一个结点
Node* curr = _root; // 引导prev走到最后一个结点
// 寻找插入位置
while (curr)
{
if (curr->_kv.first > kv.first) // 要插入的值比较小,往左走
{
prev = curr;
curr = curr->_left;
}
else if (curr->_kv.first < kv.first) // 要插入的值比根结点的值大,往右走
{
prev = curr;
curr = curr->_right;
}
else // 要插入的值比根结点的值大,则插入失败
{
return make_pair(curr,false);
}
}
// 开始插入
curr = new Node(kv);
Node* ret = curr;
if (prev->_kv.first > kv.first) // 左插入
{
prev->_left = curr;
curr->_parent = prev; //
}
else if (prev->_kv.first < kv.first) // 右插入
{
prev->_right = curr;
curr->_parent = prev; //
}
// 更新平衡因子---将新插入结点curr的祖先的平衡因子更新
// while(curr!=_root)
while (prev != nullptr)
{
if (prev->_left == curr)
{
prev->_bf--; // 如果是往其左子树插入,则_bf--;
}
else if (prev->_right == curr)
{
prev->_bf++; // 如果是往其右子树插入,则_bf++;
}
if (prev->_bf == 0) // 左右子树达到平衡,不需要继续往上更新平衡因子
{
break;
}
else if (prev->_bf == 1 || prev->_bf == -1) // 需要继续往上更新平衡因子
{
curr = prev;
prev = prev->_parent;
}
else if (prev->_bf == 2 || prev->_bf == -2) // 左右子树不平衡,需要继续旋转调整
{
if (prev->_bf == 2)
{
if (curr->_bf == 1) // 右子树高---右结点的右子树上插入
{
RotateL(prev); // 左旋转
}
else if (curr->_bf == -1) // 右子树高---右结点的左子树上插入
{
RotateRL(prev); // 先右旋再左旋
}
}
else
{
if (curr->_bf == 1) // 左子树高---左结点的右子树上插入
{
RotateLR(prev); // 先左旋再右旋
}
else if (curr->_bf == -1) // 左子树高---左结点的左子树上插入
{
RotateR(prev);
}
}
break;
}
else
{
assert(false);
}
}
return make_pair(ret, true);
}
bool Erase(const pair<K, V>& kv)
{
if (_root == nullptr) // 如果AVL树为空则不需要删除
{
return false;
}
Node* prev = nullptr; // 记录删除结点的双亲
Node* curr = _root; // 记录删除的结点
while (curr) // 寻找要删除的结点
{
if (curr->_kv.first > kv.first) // 往左寻找
{
prev = curr;
curr = curr->_left;
}
else if (curr->_kv.first < kv.first) // 往右寻找
{
prev = curr;
curr = curr->_right;
}
else // 找到了---开始删除
{
if (curr->_left == nullptr) // 删除结点的左子树为空
{
if (prev == nullptr) // 删除根结点
{
_root = curr->_right;
if (curr->_right)
{
curr->_right->_parent = nullptr;
}
}
else // 删除非根结点
{
UpdateBF(prev, curr); // 删除之前调整平衡因子
if (prev->_left == curr)
{
prev->_left = curr->_right;
}
else
{
prev->_right = curr->_right;
}
if (curr->_right)
{
curr->_right->_parent = prev;
}
}
delete curr;
return true;
}
else if (curr->_right == nullptr) // 删除结点的右子树为空
{
if (prev == nullptr) // 删除根结点,更新_root
{
_root = curr->_left;
if (curr->_left)
{
curr->_left->_parent = nullptr;
}
}
else // 删除非根结点
{
UpdateBF(prev, curr);
if (prev->_left == curr)
{
prev->_left = curr->_left;
}
else
{
prev->_right = curr->_left;
}
if (curr->_left)
{
curr->_left->_parent = prev;
}
}
delete curr;
return true;
}
else // 删除结点的左右子树都不为空
{
Node* min = curr->_right;
while (min->_left)
{
min = min->_left;
}
pair<K, V> tmp = min->_kv;
Erase(tmp); // 替换删除
curr->_kv = tmp;
return true;
}
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
Node* Find(const K& key)
{
Node* curr = _root;
while (curr)
{
if (curr->_kv.first > key)
{
curr = curr->_left;
}
else if (curr->_kv.first < key)
{
curr = curr->_right;
}
else
{
return curr;
}
}
return nullptr;
}
int AVLTreeDepth(Node* root)
{
if (root == nullptr)
{
return 0;
}
int leftDepth = AVLTreeDepth(root->_left);
int rightDepth = AVLTreeDepth(root->_right);
return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1;
}
bool IsAVLTree()
{
return _IsAVLTree(_root);
}
};