欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > 数据结构·AVL树

数据结构·AVL树

2024/10/26 3:26:29 来源:https://blog.csdn.net/atlanteep/article/details/140585865  浏览:    关键词:数据结构·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树代码

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

#include<assert.h>
#include<iostream>
using namespace std;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);//中序遍历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;}elsereturn 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--;elseparent->_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>
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;}//修改parentParentNode* 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);}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com