当前位置: 首页 > wzjs >正文

中山皇冠建设开发有限公司网站房地产三道红线

中山皇冠建设开发有限公司网站,房地产三道红线,建设银行云南分行招聘网站,青岛建设集团苏州招聘信息网站目录 一、AVL树的概念 二、AVL树的实现 (一)AVL树的结构 (二)AVL树的插入 1、AVL树插入值的过程 2、平衡因子更新 3、实现代码 (三)旋转 1、旋转的规则 2、右单旋 3、右单旋的代码实现 4、左单…

目录

一、AVL树的概念

二、AVL树的实现

(一)AVL树的结构

(二)AVL树的插入

1、AVL树插入值的过程

2、平衡因子更新

3、实现代码

(三)旋转

1、旋转的规则

2、右单旋

3、右单旋的代码实现

4、左单旋

5、左单旋的代码实现

6、左右双旋

7、左右双旋的代码实现

8、右左双旋

9、右左双旋的代码实现

(四)AVL树的一些其他代码

1、AVL树的查找

2、AVL树平衡检测

三、AVL树的调试技巧


一、AVL树的概念

        

        • AVL树是最先发明的自平衡二叉查找树,AVL是一颗空树,或者具备下列性质的⼆叉搜索树:它的左右子树都是AVL树,且左右子树的高度差的绝对值不超过1。AVL树是一颗高度平衡搜索⼆叉树,通过控制高度差去控制平衡。

        • AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis是两个前苏联的科学家,他们在1962年的论文《An algorithm for the organization of information》中发表了它。

        • AVL树实现这里引入一个平衡因子(balance factor)的概念,每个结点都有一个平衡因子,任何结点的平衡因子等于右子树的高度减去左子树的高度,也就是说任何结点的平衡因子等于0/1/-1,AVL树并不是必须要平衡因子,但是有了平衡因子可以更方便我们去进行观察和控制树是否平衡,就像一个风向标一样。

        • 为什么AVL树是高度平衡搜索二叉树,要求高度差不超过1,而不是高度差是0呢?0不是更好的平衡吗?通过画图分析会发现,不是不想这样设计,而是有些情况是做不到高度差是0的。比如一棵树是2个结点,4个结点等情况下,高度差最好就是1,无法做到高度差是0。

        • AVL树整体结点数量和分布和完全二叉树类似,高度可以控制在logN,那么增删查改的效率也可以控制在O(logN),相比二叉搜索树有了本质的提升。

        AVL树的示例图如下:

         平衡因子超过1的绝对值的情况:

 

二、AVL树的实现

(一)AVL树的结构

         代码如下所示:

template<class K, class V>
struct AVLTreeNode
{// 需要parent指针,后续更新平衡因子可以看到pair<K, V> _kv;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf; // balance factorAVLTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0){}
};template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public://AVL实现内容
private:Node* _root = nullptr;
};

(二)AVL树的插入

1、AVL树插入值的过程

        1. 插入一个值按二叉搜索树规则进行插入。

        2. 新增结点以后,只会影响祖先结点的高度,也就是可能会影响部分祖先结点的平衡因子,所以更新【从新增结点->根结点路径上的平衡因子】,实际中最坏情况下要更新到根,有些情况更新到中间就可以停止了,具体情况下面再详细分析。

        3. 更新平衡因子过程中没有出现问题,则插入结束。

        4. 更新平衡因子过程中出现不平衡,对不平衡子树旋转,旋转后调整平衡的同时,本质降低了子树的高度,不会再影响上一层,所以插入结束。

2、平衡因子更新

更新原则:

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

        • 只有子树高度变化才会影响当前结点平衡因子。

        • 插入结点,会增加高度,所以新增结点若在parent的右子树,parent的平衡因子++,若新增结点在parent的左子树,parent平衡因子--。

        • parent 所在子树的高度是否变化决定了是否会继续往上更新。

更新停止条件:

        • 更新后parent的平衡因子等于0,则更新中的parent的平衡因子变化为【-1到0】或者【1到0】,说明更新前parent子树一边高一边低,新增的结点插入在低的那边,插入后parent所在的子树高度不变,不会影响parent的父亲结点的平衡因子,更新结束。

        如下图所示,更新到中间结点,3为根的子树高度不变,不会影响上一层,更新结束:

        • 更新后parent的平衡因子等于 1 或 -1,则更新中parent的平衡因子变化为【0到1】或者【0到-1】,说明更新前parent子树两边一样高,新增的插如结点后,parent所在的子树一边高一边低,parent所在的子树符合平衡要求,但是高度增加了1,会影响parent的父亲结点的平衡因子,所以要继续向上更新。

        如下图所示:

        • 更新后parent的平衡因子等于2 或 -2,则更新中parent的平衡因子变化为【1到2】或者【-1到-2】,说明更新前parent子树一边高一边低,新增的插入结点在高的那边,parent所在的子树高的那边更高了,破坏了平衡,parent所在的子树不符合平衡要求,需要旋转处理,旋转的目标有两个:

        ① 把parent子树旋转平衡。

        ② 降低parent子树的高度,恢复到插入结点以前的高度。所以旋转后也不需要继续往上更新,插入结束。

        如下图所示,更新到10结点,平衡因子为2,10所在的子树已经不平衡,需要旋转处理:

        • 不断更新,更新到根,根的平衡因子是1或-1也停止了。

3、实现代码

//节点的插入:
//①插入 ②更新平衡因子 ③旋转
bool 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 (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = 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){// 不平衡了,旋转处理break;}else{//若出现其他情况直接断言断死,就知道是在这里出错了assert(false);}}return true;
}

(三)旋转

1、旋转的规则

        1. 保持搜索树的规则

        2. 让旋转的树从不平衡变平衡,其次降低旋转树的高度

        旋转总共分为四种,左单旋/右单旋/左右双旋/右左双旋。

        说明:下面的图中,有些结点我们给的是具体值,如10和5等结点,这里是为了方便讲解,实际中是什么值都可以,只要大小关系符合搜索树的性质即可。

2、右单旋

        • 本图1展示的是10为根的树,有a/b/c抽象为三棵高度为h的子树(h>=0),a/b/c均符合AVL树的要求。10可能是整棵树的根,也可能是一个整棵树中局部的子树的根。这里a/b/c是高度为h的子树,是一种概括抽象表示,他代表了所有右单旋的场景,实际右单旋形态有很多种,具体图2/图3/图4/图5进行了详细描述。 

        • 在a子树中插如一个新结点,导致a子树的高度从h变成h+1,不断向上更新平衡因子,导致10的平衡因子从-1变成-2,10为根的树左右嘎婆度差超过1的绝对值,违反平衡规则。10为根的树左边太高了,需要往右边旋转,控制两棵树的平衡。

        • 旋转核心步骤,因为5 < b子树的值 < 10,将b变成10的左子树,10变成5的右子树,5变成这棵树新的根,符合搜索树的规则,控制了平衡,同时这棵的高度恢复到了插入之前的h+2,符合旋转原则。如果插如之前10整棵树的一个局部子树,旋转后不会再影响上一层,插入结束了。

                                                                   图1,右单旋的过程

                                 图2/3/3/4/5,展示右单旋的多种情况,所以要抽象为a/b/c三个子树

3、右单旋的代码实现

//右单旋实现代码
void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;// 需要注意除了要修改孩子指针指向,还要修改父亲parent->_left = subLR;if (subLR)//subLR(b子树)可能为空,要做判断subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;// parent有可能是整棵树的根,也可能是局部的子树// 如果是整棵树的根,要修改_root// 如果是局部的指针要跟上⼀层链接if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}parent->_bf = subL->_bf = 0;
}

4、左单旋

        • 本图6展示的是10为根的树,有a/b/c抽象为三棵高度为h的子树(h>=0),a/b/c均符合AVL树的要求。10可能是整棵树的根,也可能是一个整棵树中局部的子树的根。这里a/b/c是高度为h的子树,是一种概括抽象表示,他代表了所有左单旋的场景,实际右左单旋形态有很多种,具体跟上面右旋类似。

        • 在a子树中插入一个新结点,导致a子树的高度从h变成h+1,不断向上更新平衡因子,导致10的平衡因子从1变成2,10为根的树左右高度差超过1的绝对值,违反平衡规则。10为根的树右边太高了,需要往左边旋转,控制两棵树的平衡。

        • 旋转核心步骤,因为10 < b子树的值 < 15,将b变成10的右子树,10变成15的左子树,15变成这棵树新的根,符合搜索树的规则,控制了平衡,同时这棵的高度恢复到了插入之前的h+2,符合旋转原则。如果插入之前10整棵树的一个局部子树,旋转后不会再影响上一层,插入结束了。

                                                                           图6

5、左单旋的代码实现

//左单旋实现代码
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}parent->_bf = subR->_bf = 0;
}

6、左右双旋

        通过图7和图8可以看到,左边高时,如果插入位置不是在a子树,而是插入在b子树,b子树高度从h变成h+1,引发旋转,右单旋无法解决问题,右单旋后,我们的树依旧不平衡。右单旋解决的纯粹的左边高,但是插入在b子树中,10为根的子树不再是单纯的左边高,对于10是左边高,但是对于5是右边高,需要用两次旋转才能解决,以5为旋转点进行一个左单旋,以10为旋转点进行一个右单旋,这棵树这棵树就平衡了。

                                                                           图7

                                                                         图8

 

        • 图7和图8分别为左右双旋中h==0和h==1具体场景分析,下面我们将a/b/c子树抽象为高度h的AVL子树进行分析,另外我们需要把b子树的细节进⼀步展开为8和左子树高度为h-1的e和f子树,因为我们要对b的父亲节点5为旋转点进行左单旋,左单旋需要动b树中的左子树。b子树中新增结点的位置不同,平衡因子更新的细节也不同,通过观察8的平衡因子不同,这子我们要分三个场景讨论。

        • 场景1:h >= 1时,新增结点插入在e子树,e子树高度从h-1并为h并不断更新8->5->10平衡因子,引发旋转,其中8的平衡因子为-1,旋转后8和5平衡因子为0,10平衡因子为1。

        • 场景2:h >= 1时,新增结点插入在f子树,f子树高度从h-1变为h并不断更新8->5->10平衡因子,引发旋转,其中8的平衡因子为1,旋转后8和10平衡因子为0,5平衡因子为-1。

        • 场景3:h == 0时,a/b/c都是空树,b自己就是一个新增结点,不断更新5->10平衡因子,引发旋转,其中8的平衡因子为0,旋转后8和10和5平衡因子均为0。

7、左右双旋的代码实现

        先左单旋然后右单旋,再修改平衡因子。

//左右双旋
void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0)//b子树为空的情况{subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else if (bf == -1)//新增节点插入到b子树的左子树e情况{subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;}else if (bf == 1)//新增节点插入到b子树的右子树f情况{subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}elseassert(false);
}

8、右左双旋

        • 跟左右双旋类似,下面我们将a/b/c子树抽象为高度h的AVL子树进行分析,另外我们需要把b子树的细节进一步展开为12和左子树高度为h-1的e和f子树,因为我们要对b的父亲15为旋转点进行右单旋,右单旋需要动b树中的右子树。b子树中新增结点的位置不同,平衡因子更新的细节也不同,通过观察12的平衡因子不同,这里我们要分三个场景讨论。

        • 场景1:h >= 1时,新增结点插入在e子树,e子树高度从h-1变为h并不断更新12->15->10平衡因子,引发旋转,其中12的平衡因子为-1,旋转后10和12平衡因子为0,15平衡因子为1。

        • 场景2:h >= 1时,新增结点插入在f子树,f子树高度从h-1变为h并不断更新12->15->10平衡因子,引发旋转,其中12的平衡因子为1,旋转后15和12平衡因子为0,10平衡因子为-1。

        • 场景3:h == 0时,a/b/c都是空树,b自己就是一个新增结点,不断更新15->10平衡因子,引发旋转,其中12的平衡因子为0,旋转后10和12和15平衡因子均为0。

9、右左双旋的代码实现

//右左双旋
void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0)//b子树为空的情况{subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 1)//新增节点插入到b子树的右子树f情况{subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if (bf == -1)//新增节点插入到b子树的左子树e情况{subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;}else{assert(false);}
}

(四)AVL树的一些其他代码

1、AVL树的查找

        拿二叉搜索树逻辑实现即可,搜索效率为 O(logN)。

        代码实现如下:

Node* Find(const K& key)
{Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}elsereturn cur;}return nullptr;
}

 

2、AVL树平衡检测

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;
}
bool _IsBalanceTree(Node* root)
{// 空树也是AVL树if (nullptr == root)return true;// 计算pRoot结点的平衡因子:即pRoot左右子树的高度差int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者// pRoot平衡因子的绝对值超过1,则一定不是AVL树if (abs(diff) >= 2){cout << root->_kv.first << "⾼度差异常" << endl;return false;}if (root->_bf != diff){cout << root->_kv.first << "平衡因⼦异常" << endl;return false;}// pRoot的左和右如果都是AVL树,则该树一定是AVL树return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}

三、AVL树的调试技巧

        AVL树的调试比较麻烦,需要以下技巧:先进行初步筛查,再进行细致筛查。可以先打印出来(没插入一个值就检测一次平衡)

        然后手动打一个断点:写一个判断语句,值等于插入故障值的时候随便写一条语句,然后在这条语句上打一个断点。

        然后手动画出这棵树,再一步步对比哪里出问题。


        以上内容仅供分享,若有错误,请多指正。


文章转载自:

http://MvEIFLkG.hgwsj.cn
http://DfCDtX7W.hgwsj.cn
http://mpZ5TmDX.hgwsj.cn
http://zGpzdgvz.hgwsj.cn
http://CBpoZEEa.hgwsj.cn
http://pdGGMZDF.hgwsj.cn
http://tRcgRaPv.hgwsj.cn
http://3Aqjgyon.hgwsj.cn
http://VeW79RxO.hgwsj.cn
http://AwsfBua5.hgwsj.cn
http://UobkUmWl.hgwsj.cn
http://ihzJuYsl.hgwsj.cn
http://gdnVyb3X.hgwsj.cn
http://M0KHlpz5.hgwsj.cn
http://1goQER3G.hgwsj.cn
http://PZ724cM1.hgwsj.cn
http://NJr2l414.hgwsj.cn
http://nto1OANZ.hgwsj.cn
http://UeZ8bTMV.hgwsj.cn
http://3mVKVPAN.hgwsj.cn
http://0yzcv0VT.hgwsj.cn
http://3KcOiCSF.hgwsj.cn
http://90AKMbqD.hgwsj.cn
http://1Q4HCzey.hgwsj.cn
http://PRLPWPEc.hgwsj.cn
http://EeRfd6AK.hgwsj.cn
http://ddxon2Mm.hgwsj.cn
http://kUfT4CqT.hgwsj.cn
http://J0D5cfBM.hgwsj.cn
http://vY1Hl7Ma.hgwsj.cn
http://www.dtcms.com/wzjs/629606.html

相关文章:

  • 哪个网站做螺丝生意好西丽网站设计
  • 手机网站排名优化建站网站源码
  • 桥头镇网站建设ps怎么做网站特效
  • 泰安建设厅网站音乐网站设计总结
  • 泰州网站建设物美价廉汕头市潮南区紧急提醒
  • 网站后台登陆密码破解做dnf辅助官方网站
  • 牡丹江网站建设公司建站公司成功案例
  • wordpress网站二次开发中文域名指向同一个网站
  • 长春网站建设新格郑州短视频拍摄公司
  • 广东住房和城乡建设厅网站企业所得税怎么算例题
  • 无锡网站科技公司网站整站优化
  • 本地做网站图片怎么存怎么建wordpress主题的子主题
  • 扬州网站建设培训html网站优化
  • 兰州做家教去哪个网站比较好如何做网站规划
  • 做网站 网站违法吗网络服务提供商有哪些
  • 营销型网站建设公司哪家好哪个好哪里好湖南住房与城乡建设厅网站
  • 公司如何做网站宣传可以做cps合作的棋牌网站
  • 沧州模板建站平台揭阳做网站哪个好
  • 浙江省和住房建设厅网站河南省住房和城乡建设厅网站
  • 东莞网站建设总部地址国外有哪些做服装的网站有哪些方面
  • 机械加工网站色彩搭配那个网站做车险分期
  • 可信赖的顺的网站建设健康码更新视频
  • 淘宝优惠券私人查券网站怎么做厦门网格员
  • 如何登录国外购物网站seo应该如何做
  • 国家骨干高职院校建设网站东莞营销型高端网站建设
  • 成都 企业 网站制作建公司网站一般多少钱
  • 网站域名空间5个G的多少钱wordpress转移过电脑
  • 坂田网站设计广告发光字制作培训班
  • 企业网站开发方案注册公司流程 上海
  • 海外产品网站建设泉州工程建设招投标信息网