织梦后台点击网站主页,seo设置是什么,手机app微信网站,艺术网站建设模板目录
一、AVL树的概念
二、AVL树的实现
1.AVL树节点的定义
2.AVL树的插入
3.AVL树的旋转
4.AVL树的验证
三、AVL树的性能
四、完结撒❀ 一、AVL树的概念 二叉搜索树虽可以缩短查找的效率#xff0c;但 如果数据有序或接近有序二叉搜索树将退化为单支树#xff0c;查 …目录
一、AVL树的概念
二、AVL树的实现
1.AVL树节点的定义
2.AVL树的插入
3.AVL树的旋转
4.AVL树的验证
三、AVL树的性能
四、完结撒❀ 一、AVL树的概念 二叉搜索树虽可以缩短查找的效率但 如果数据有序或接近有序二叉搜索树将退化为单支树查 找元素相当于在顺序表中搜索元素效率低下 。因此两位俄罗斯的数学家 G.M.Adelson-Velskii 和 E.M.Landis 在 1962 年 发明了一种解决上述问题的方法 当向二叉搜索树中插入新结点后如果能保证每个结点的左右 子树高度之差的绝对值不超过 1( 需要对树中的结点进行调整 ) 即可降低树的高度从而减少平均 搜索长度。 一棵 AVL 树或者是空树或者是具有以下性质的二叉搜索树 ● 它的左右子树都是 AVL 树 ● 左右子树高度之差 ( 简称平衡因子 ) 的绝对值不超过 1(-1/0/1 如果一棵二叉搜索树是高度平衡的它就是 AVL 树。如果它有 n 个结点其高度可保持在 O(log2 n) 搜索时间复杂度 O(log2 n) 。 二、AVL树的实现
1.AVL树节点的定义
AVL树节点的定义
templateclass T
struct AVLTreeNode
{AVLTreeNode(const T data): _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _bf(0){}AVLTreeNodeT* _pLeft; // 该节点的左孩子AVLTreeNodeT* _pRight; // 该节点的右孩子AVLTreeNodeT* _pParent; // 该节点的双亲T _data;int _bf; // 该节点的平衡因子
};
2.AVL树的插入 AVL 树就是在二叉搜索树的基础上引入了平衡因子因此 AVL 树也可以看成是二叉搜索树。那么 AVL 树的插入过程可以分为两步 1. 按照二叉搜索树的方式插入新节点 2. 调整节点的平衡因子 bool Insert(const pairK, V kv)
{// 1. 先按照二叉搜索树的规则将节点插入到AVL树中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-_left;}else if (cur-_kv.first kv.first){parent cur;cur cur-_right;}else{//插入相同值return false;}}//找到cur所在位置cur new Node(kv);if (parent-_kv.first cur-_kv.first){parent-_left cur;cur-_parent parent;}else{parent-_right cur;cur-_parent parent;}// 2. 新节点插入后AVL树的平衡性可能会遭到破坏此时就需要更新平衡因子并检测是否//破坏了AVL树的平衡性/*pCur插入后pParent的平衡因子一定需要调整在插入之前pParent的平衡因子分为三种情况-10, 1, 分以下两种情况1. 如果pCur插入到pParent的左侧只需给pParent的平衡因子-1即可2. 如果pCur插入到pParent的右侧只需给pParent的平衡因子1即可此时pParent的平衡因子可能有三种情况0正负1 正负21. 如果pParent的平衡因子为0说明插入之前pParent的平衡因子为正负1插入后被调整成0此时满足
AVL树的性质插入成功2. 如果pParent的平衡因子为正负1说明插入前pParent的平衡因子一定为0插入后被更新成正负1此
时以pParent为根的树的高度增加需要继续向上更新3. 如果pParent的平衡因子为正负2则pParent的平衡因子违反平衡树的性质需要对其进行旋转处理*///更新平衡因子while (parent){if (parent-_left cur){parent-_bf--;}else{parent-_bf;}if (parent-_bf 0){//二叉树高度没问题更新结束break;}else if (parent-_bf 1 || parent-_bf -1){// 插入前双亲的平衡因子是0插入后双亲的平衡因子为1 或者 -1 说明以双亲为根的二叉树// 的高度增加了一层因此需要继续向上调整cur parent;parent parent-_parent;}else if (parent-_bf -2 || parent-_bf 2){//双亲的平衡因子为正负2违反了AVL树的平衡性//二叉树平衡被破坏需要旋转完成平衡//判断是右单旋还是左单旋还是双旋//右单旋if (parent-_bf -2 cur-_bf -1){//...}//左单旋else if (parent-_bf 2 cur-_bf 1){//...}//左右双旋else if (parent-_bf -2 cur-_bf 1){//...}//右左双旋else if (parent-_bf 2 cur-_bf -1){//...}}else{//理论上不会出现这种状况assert(false);}}return true;
}
3.AVL树的旋转 如果在一棵原本是平衡的 AVL 树中插入一个新节点可能造成不平衡此时必须调整树的结构 使之平衡化。根据节点插入位置的不同 AVL 树的旋转分为四种 1) 新节点插入较高左子树的左侧---左左右单旋 /*上图在插入前AVL树是平衡的新节点插入到30的左子树(注意此处不是左孩子)中30左
子树增加了一层导致以60为根的二叉树不平衡要让60平衡只能将60左子树的高度减少一层右子
树增加一层即将左子树往上提这样60转下来因为60比30大只能将其放在30的右子树而如果30有
右子树右子树根的值一定大于30小于60只能将其放在60的左子树旋转完成后更新节点
的平衡因子即可。在旋转过程中有以下几种情况需要考虑1. 30节点的右孩子可能存在也可能不存在2. 60可能是根节点也可能是子树如果是根节点旋转完成后要更新根节点如果是子树可能是某个节点的左子树也可能是右子树此处可举一些详细的例子进行画图考虑各种情况加深旋转的理解
*/
void _RotateR(Node Parent)
{// SubL: Parent的左孩子// SubLR: Parent左孩子的右孩子注意该Node SubL Parent-_Left;Node SubLR SubL-_Right;// 旋转完成之后30的右孩子作为双亲的左孩子Parent-_Left SubLR;// 如果30的左孩子的右孩子存在更新亲双亲if (SubLR)SubLR-_Parent Parent;// 60 作为 30的右孩子SubL-_Right Parent;// 因为60可能是棵子树因此在更新其双亲前必须先保存60的双亲Node Parent Parent-_Parent;// 更新60的双亲Parent-_Parent SubL;// 更新30的双亲SubL-_Parent Parent;// 如果60是根节点根新指向根节点的指针if (NULL Parent){_root SubL;SubL-_Parent NULL;}else{// 如果60是子树可能是其双亲的左子树也可能是右子树if (Parent-_Left Parent)Parent-_Left SubL;elseParent-_Right SubL;}// 根据调整后的结构更新部分节点的平衡因子Parent-_bf SubL-_bf 0;
}
2) 新节点插入较高右子树的右侧---右右左单旋 //左单旋
void LNode(Node* parent)
{/*if (parent _root){Node* pparent nullptr;}else{Node* pparent parent-_parent;}*/Node* pparent parent-_parent;Node* subR parent-_right;Node* subRL subR-_left;parent-_left subRL;if (subRL)subRL-_parent parent;subR-_left parent;parent-_parent subR;if (pparent){subR-_parent pparent;if (pparent-_left parent){pparent-_left subR;}else{pparent-_right subR;}}else{_root subR;subR-_parent nullptr;}parent-_bf subR-_bf 0;
} 3) 新节点插入较高左子树的右侧---左右先左单旋再右单旋 将双旋变成单旋后再旋转即 先对 30 进行左单旋然后再对 90 进行右单旋 旋转完成后再 考虑平衡因子的更新。 // 旋转之前60的平衡因子可能是-1/0/1旋转完成之后根据情况对其他节点的平衡因子进
//行调整
void _RotateLR(Node Parent)
{Node SubL Parent-_Left;Node SubLR SubL-_Right;// 旋转之前保存pSubLR的平衡因子旋转完成之后需要根据该平衡因子来调整其他节点的平衡因子int bf SubLR-_bf;// 先对30进行左单旋_RotateL(Parent-_Left);// 再对90进行右单旋_RotateR(Parent);if (1 bf)SubL-_bf -1;else if (-1 bf)Parent-_bf 1;
}
4) 新节点插入较高右子树的左侧---右左先右单旋再左单旋 //右左双旋
void RLNode(Node* parent)
{Node* subR parent-_right;Node* subRL subR-_left;int bf subRL-_bf;RNode(parent-_right);LNode(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 为根的子树不平衡即 Parent 的平衡因子为 2 或者 -2 分以下情况考虑 1Parent 的平衡因子为 2 说明 Parent 的右子树高设 Parent 的右子树的根为 SubR 当 SubR 的平衡因子为 1 时执行左单旋 当 SubR 的平衡因子为 -1 时执行右左双旋 2Parent 的平衡因子为 -2 说明 Parent 的左子树高设 Parent 的左子树的根为 SubL 当 SubL 的平衡因子为 -1 是执行右单旋 当 SubL 的平衡因子为 1 时执行左右双旋 旋转完成后原 Parent 为根的子树个高度降低已经平衡不需要再向上更新。 4.AVL树的验证 AVL 树是在二叉搜索树的基础上加入了平衡性的限制因此要验证 AVL 树可以分两步 1. 验证其为二叉搜索树 如果中序遍历可得到一个有序的序列就说明为二叉搜索树 2. 验证其为平衡树 ● 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子 ) ● 节点的平衡因子是否计算正确 int _size(Node* root)
{return root nullptr ? 0 : _size(root-_left) _size(root-_right) 1
}int _Height(Node* root)
{if (root nullptr){return 0;}return max(_Height(root-_left), _Height(root-_right)) 1;
}bool _IsBalance(Node* root)
{if (root nullptr){return true;}int LeftHeight _Height(root-_left);int RightHeight _Height(root-_right);if (abs(LeftHeight - RightHeight) 2){return false;}//可以顺便再检查一下平衡因子if (abs(LeftHeight - RightHeight) ! root-_bf){cout root-_kv.first endl;return false;}return _IsBalance(root-_left) _IsBalance(root-_right);
}
三、AVL树的性能 AVL 树是一棵绝对平衡的二叉搜索树其要求每个节点的左右子树高度差的绝对值都不超过 1 这 样可以保证查询时高效的时间复杂度即 log2 N 。但是如果要对 AVL 树做一些结构修改的操 作性能非常低下比如插入时要维护其绝对平衡旋转的次数比较多更差的是在删除时 有可能一直要让旋转持续到根的位置。因此如果需要一种查询高效且有序的数据结构而且数 据的个数为静态的 ( 即不会改变 ) 可以考虑 AVL 树但一个结构经常修改就不太适合。 四、完结撒❀
如果以上内容对你有帮助不妨点赞支持一下以后还会分享更多编程知识我们一起进步。 最后我想讲的是据说点赞的都能找到漂亮女朋友❤