一.AVL树概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1( 可以为-1/0/1 )

二.AVL树模拟实现
AVL树接口总览
template<class K,class V>
struct AVLTreeNode
{
AVLTreeNode(const pair<K,V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
,_kv(kv)
{
}
AVLTreeNode* _left; // 指向左子树节点
AVLTreeNode* _right; // 指向右子树节点
AVLTreeNode* _parent; // 指向父节点
int _bf; // 平衡因子
pair<K, V> _kv; // 节点数据
};
template<class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree()
:_root(nullptr)
{
}
~AVLTree();
Node* Find(const K& key);
V& operator[](const K& key);
bool IsAVLTree();
void InOrder();
bool Insert(const pair<K, V>& kv);
void RotateL(Node* parent);
void RotateR(Node* parent);
void RotateLR(Node* parent);
void RotataRL(Node* parent);
private:
Node* _root;
};
当插入一个新节点的时候,首先根据二叉搜索树的性质,比根节点小的去左子树查找,比根节点大的去右子树查找,循环找到新节点的位置,如果是二叉搜索树的话,插入操作到此就完成了,但AVL树需要保证平衡因子的绝对值不超过1,所以我们还需要更新 新节点沿着父节点的路径上的平衡因子,如果有绝对值超过2的平衡因子,则需要进行旋转操作

右单旋 :

void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* parentParent = parent->_parent;
parent->_left = subLR;
subL->_right = parent;
if (subLR)
subLR->_parent = parent;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (parentParent->_right == parent) parentParent->_right = subL;
else parentParent->_left = subL;
subL->_parent = parentParent;
}
parent->_parent = subL;
subL->_bf = parent->_bf = 0;
}
左单旋 :

void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* parentParent = parent->_parent;
parent->_right = subRL;
subR->_left = parent;
if (subRL)
subRL->_parent = parent;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (parentParent->_right == parent) parentParent->_right = subR;
else parentParent->_left = subR;
subR->_parent = parentParent;
}
parent->_parent = subR;
subR->_bf = parent->_bf = 0;
}
左右双旋 :

void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
if (bf == 1)
{
parent->_bf = 0;
subLR->_bf = 0;
subL->_bf = -1;
}
else if (bf == -1)
{
parent->_bf = 1;
subLR->_bf = 0;
subL->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = 0;
subLR->_bf = 0;
subL->_bf = 0;
}
}
右左双旋 :

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 = 0;
subRL->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 0;
subR->_bf = 1;
subRL->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = 0;
subR->_bf = 0;
subRL->_bf = 0;
}
}
插入操作
pair<Node*, bool> Insert(const pair<K, V>& kv)
{
// 完成插入操作
if (_root == nullptr)
{
_root = new Node(kv);
return pair<Node*,bool>(_root,true);
}
Node* parent = _root, * cur = _root;
while (cur)
{
if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return pair<Node*,bool>(cur, false);
}
}
cur = new Node(kv);
if (parent->_kv.first < cur->_kv.first) parent->_right = cur;
else parent->_left = cur;
cur->_parent = parent;
Node* tmp = cur;
// 更新平衡因子
while (cur != _root)
{
if (parent->_kv.first < cur->_kv.first) ++parent->_bf;
else --parent->_bf;
if (parent->_bf == 0) break;
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = cur->_parent;
}</

本文详细介绍了AVL树和红黑树的概念,这两种自平衡二叉搜索树能够保证查找效率。AVL树通过保持左右子树高度差不超过1来保持平衡,插入操作后可能需要进行旋转调整。红黑树则通过颜色规则确保最长路径不超过最短路径的两倍,插入操作中涉及变色和旋转操作。此外,文章还展示了如何使用AVL树和红黑树实现map和set的基本操作。
最低0.47元/天 解锁文章

1033





