自学内容网 自学内容网

数据结构·AVL树

1. AVL树的概念

        二叉搜索树虽可以缩短查找的效率,但如果存数据时接近有序,二叉搜索将退化为单支树,此时查找元素效率相当于在顺序表中查找,效率低下。因此两位俄罗斯数学家 G.M.Adelson-Velskii 和E.M.Landis 在1962年发明了一种解决上述问题的方法:当二叉搜索树中插入新节点后,如果能保证每个节点的左右子树高度差不大于 1 的绝对值,即可降低树的高度,从而减少平均搜索长度。

        一个标准的AVL树任意节点的左右子树高度差的绝对值不大于1,我们将记录高度差的数据称为平衡因子。

                                        平衡因子 = 右子树高度 - 左子树高度

                                ​​​​​​​        

2. AVL树节点的定义

                ​​​​​​​        

        我们根STL库保持一致,将key和value合并定义成pair类型,再在节点中添加一个平衡因子。

3. AVL树的插入

        关于平衡因子,我们可以通过其定义得知这样两条条性质:

        1. 插入节点时只会影响到祖先的平衡因子,而不会影响到其他节点的平衡因子。

        2. 父节点右侧插入节点其平衡因子 +1,左侧插入节点其平衡因子 -1

        对于插入节点的父节点来说,插入后父节点的平衡因子只会出现3种情况:第一种情况是平衡因子为0 、第二种情况是平衡因子为1 或 -1 、第三种情况是有祖先平衡因子出现 2 或 -2

        插入后父节点平衡因子为0时

        说明在左侧或右侧插入一个节点后,父节点平衡了,但此时并不会影响除父节点外的所有祖先的平衡因子,因此平衡因子不需要再向上更新了。

        ​​​​​​​        ​​​​​​​        ​​​​​​​        

        插入后父节点平衡因子为 1 或 -1 时

        说明父节点之前的平衡因子一定为0,左侧插入能使父节点变-1,右侧插入能使父节点变1。为什么父节点平衡因子之前一定为0?因为AVL树的平衡因子只可能出现-1、0、1三种情况。

        此时平衡因子需要向上更新

        ​​​​​​​        ​​​​​​​        ​​​​​​​        

        插入后有祖先平衡因子出现 2 或 -2

        说明这个祖先更新前是1或-1,新节点插入在高的那棵树上,进一步加剧了高差,此时已经违反高差不大于1的规则了,此时需要旋转处理

        ​​​​​​​        ​​​​​​​        ​​​​​​​        

        平衡因子调整的逻辑就是这样添加到insert函数中的,我会把完整的代码贴到最后的

        ​​​​​​​        ​​​​​​​        ​​​​​​​        

4. AVL树的旋转

4.1 左单旋

        插入节点在较高右子树的右侧时就要进行左单旋。

        我这个图中的长方形就代表一颗子树。下面外我们基于这个旋转逻辑编写代码

        经过我们前面更新平衡因子之后找到不符合规则的父节点30,我们进行旋转代码编写的时候不仅仅要弄节点向下的链接内容,还要记得修改节点的parent指针,也就是向上链接的内容。

        我们要更改的主要就是 30(parent节点) 60(subR节点) tree_b(subRL)

                ​​​​​​​        ​​​​​​​        

        但是我们把代码写成这个还是不对的,因为parent的parent没有向下链接。在它向下链接的过程中还有两种情况:就是parent本身就是整颗树的根节点,旋转后subR成为整棵树的根节点;或parent只是树中的一个普通节点,那就要考虑parent的parent向下链接的时候链接左子树还是右子树了。

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​​​​​​​​

4.2 右单旋

        当要在较高左子树的左侧插入新节点就要进行右单旋

        右单旋的思路和代码和左单旋是一样的,反过来而已,先向下更新链接内容,再向上更新链接内容,然后更新parentParent的向下链接,最后调整平衡因子

        ​​​​​​​        ​​​​​​​        ​​​​​​​        

4.3 右左双旋

        当新节点插入在较高右子树的左侧时就要进行右左双旋。

        如果我们单纯以30为parent或者说旋转基点进行单左旋,结果就是60这棵子树变成30的右子树,但是这棵树还是不平衡的,c比d高出了2,此时再以90为基点右旋结果就是又回去了。

        当较高的树在最两侧的时候我们进行单左旋或单右旋是可以让树平衡的,但是如果较高的树在中间的时候我们进行单左右旋就会让这个较高树一直在中间来回变动,而树一直不会平衡。

        所以AVL就用我图中的方案解决了这一问题。

        ​​​​​​​        

        但是如果我们直接把代码写成这样肯定是不行的,因为平衡因子的问题还没有得到更新。

        如果是我图中画的情况也就是说插入在c树中最后的平衡因子就是 -1、0、0

        如果新节点插入在b树中,最后平衡因子就应该是 0、0、1

        如果 h=0,也就是说60是新插入的节点,最后的平衡因子就应该是 0、0、0

        那具体是这三种情况中的哪种,我们可以通过观察插入后60节点的平衡因子来判断

        ​​​​​​​        ​​​​​​​        ​​​​​​​​​​​​​​

4.4 左右双旋

        当在较高左子树右侧插入新节点时要进行左右双旋

        代码与右左双旋一个思路,注意最后要调整平衡因子。

                        

5. AVL树代码

        删除的思路和插入是一致的,但是删除的更新平衡因子会比较复杂,因为删除在旋转了之后平衡因子可能还要继续向上更新。今天先不写删除了,如果之后有精力我会把删除更新上的。

template<class K, class V>
struct AVLTNode
{

AVLTNode(const pair<K, V>& kv)
: _kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
{}

pair<K, V> _kv;
AVLTNode<K, V>* _left;
AVLTNode<K, V>* _right;
AVLTNode<K, V>* _parent;
int _bf;//balance factor 平衡因子
};



template<class K, class V>
class AVLTree
{
typedef AVLTNode<K, V> Node;

public:

//构造
AVLTree() = default;
//拷贝构造
AVLTree(const AVLTree<K, V>& t)
{
_root = Copy(t._root);
}

//赋值运算符重载
void operator=(const AVLTree<K, V>& t)
{
AVLTree<K, V> new_t(t);
std::swap(new_t._root, _root);
}

//析构
~AVLTree()
{
Destroy(_root);
_root = nullptr;
}

void Destroy(Node* root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
Node* Copy(const Node* root)
{
if (root == nullptr)
return nullptr;
Node* newnode = new Node(root->kv);
newnode->_left = Copy(root->_left);
newnode->_right = Copy(root->_right);

return newnode;
}



//插入
bool Insert(const pair<K, V>& kv);

//搜索
Node* Find(const K& x);

//删除
bool Erase(const K& x);

//中序遍历
void InOrder()
{
_InOrder(_root);
cout << endl;
}

//树的高度
int Height()
{
return _Height(_root);
}

//统计节点总个数(插入时可能会有重复数据)
int Size()
{
return _Size(_root);
}

private:

//左单旋
void RotateL(Node* parent);
//右单旋
void RotateR(Node* parent);
//右左双旋
void RotateRL(Node* parent);
//左右双旋
void RotateLR(Node* parent);

//中序遍历(子函数)
void _InOrder(Node* root)
{
if (root == nullptr)
return;

_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_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;
}

//统计节点总个数(插入时可能会有重复数据)
int _Size(Node* root)
{
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}

private:
Node* _root = nullptr;
};



//插入
template<class K, class V>
bool AVLTree<K, V>::Insert(const pair<K, V>& kv)
{
//链表为空特殊处理
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}

Node* parent = nullptr;
Node* cur = _root;
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 (cur->_kv.first < parent->_kv.first)
{
parent->_left = cur;
cur->_parent = parent;
}
else
{
parent->_right = cur;
cur->_parent = parent;
}

//更新平衡因子
while (parent)
{
if (cur == parent->_left)
parent->_bf--;
else
parent->_bf++;

if (parent->_bf == 0)
break;
else if (parent->_bf == -1 || parent->_bf == 1)
{
//继续向上更新
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == -2 || parent->_bf == 2)
{
//不平衡了,旋转处理
if (parent->_bf == 2 && cur->_bf == 1)//左旋情况
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1)//右旋情况
{
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)//右左双旋
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)//左右双旋
{
RotateLR(parent);
}
break;//旋完就退出更新
}
else
{
//出现其他奇奇怪怪的情况直接报错
assert(false);
}
}

return true;
}


//搜索
template<class K, class V>
AVLTNode<K, V>* AVLTree<K, V>::Find(const K& x)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < x)
{
cur = cur->_right;
}
else if (cur->_kv.first > x)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}




//删除
template<class K, class V>
bool AVLTree<K, V>::Erase(const K& x)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < x)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > x)
{
parent = cur;
cur = cur->_left;
}
else
{
//找到了,开始删除
goto Select_Deletion_Method_cur;
}
}
return false;

//删除的情况选择
Select_Deletion_Method_cur:
if (cur->_left == nullptr)
{
//第一种陷阱
if (parent == nullptr)
{
_root = cur->_right;
delete cur;
return true;
}
if (cur->_kv.first < parent->_kv.first)
{
//删左节点
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
//第一种陷阱
if (parent == nullptr)
{
_root = cur->_left;
delete cur;
return true;
}
if (cur->_kv.first < parent->_kv.first)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
delete cur;
return true;
}
else //有两个孩子
{
//用右子树的最小节点作为替代节点
Node* rightMinP = nullptr;
Node* rightMin = cur->_right;
int flag = 0;
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
++flag;
}
cur->_kv.first = rightMin->_kv.first;
//第二种陷阱
if (flag == 0)
{
cur->_right = rightMin->_right;
delete rightMin;
return true;
}
rightMinP->_left = rightMin->_right;
delete rightMin;
return true;
}
}



//左单旋
template<class K, class V>
void AVLTree<K, V>::RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = parent->_right->_left;

//修改向下链接内容
parent->_right = subRL;
subR->_left = parent;
//修改向上链接内容
subR->_parent = parent->_parent;
parent->_parent = subR;
if (subRL)//防止该树点为空
{
subRL->_parent = parent;
}

//parent的parent向下链接
Node* parentParent = subR->_parent;
if (parentParent == nullptr)//整棵树的根
{
_root = subR;
}
else
{
if (parent == parentParent->_right)
{
parentParent->_right = subR;
}
else
{
parentParent->_left = subR;
}
}
//调整平衡因子
parent->_bf = 0;
subR->_bf = 0;
}

//右单旋
template<class K, class V>
void AVLTree<K, V>::RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;

//修改向下链接内容
parent->_left = subLR;
subL->_right = parent;
//修改向上链接属性
subL->_parent = parent->_parent;
parent->_parent = subL;
if (subLR)
{
subLR->_parent = parent;
}
//修改parentParent
Node* parentParent = subL->_parent;
if (parentParent == nullptr)
{
_root = subL;
}
else
{
if (parent == parentParent->_right)
{
parentParent->_right = subL;
}
else
{
parentParent->_left = subL;

}
}
//更新平衡因子
subL->_bf = 0;
parent->_bf = 0;
}

//右左双旋
template<class K, class V>
void AVLTree<K, V>::RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;

RotateR(parent->_right);
RotateL(parent);
//更新平衡因子
if (bf == 0)
{
parent->_bf = 0;
subR->_bf = 0;
subRL->_bf = 0;
}
else 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
{
assert(false);
}
}

//左右双旋
template<class K, class V>
void AVLTree<K, V>::RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;

RotateL(parent->_left);
RotateR(parent);
//更新平衡因子
if (bf == 0)
{
parent->_bf = 0;
subL->_bf = 0;
subLR->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = 0;
subLR->_bf = 0;
}
else
{
assert(false);
}
}

原文地址:https://blog.csdn.net/atlanteep/article/details/140585865

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!