平衡二叉树 之 AVL树

AVL树是最先发明的自平衡二叉查找树。AVL树以其发明者前苏联学者 G.M. Adelson-Velsky 和 E.M. Landis 名字而命名,他们在1962年的论文《An algorithm for the organization of information》中发表了它。[参考WiKI]

AVL树中,一个非常重要的概念为平衡因子(Balance factor),对于任意节点 x ,其平衡因子定义为该节点右子树和左子树高度差,即 bf(x)=h(x-right)-h(x-left)

带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

1. AVL树数据结构

为了方便计算每个节点的平衡因子,对二叉树的数据结构进行修改,增加一个数据单元用于记录以该节点为root的子树高度,重新定义数据结构如下:
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. struct BinaryTreeNode {  
  2.     keyType key;  
  3.     int height; //记录以该节点为root的树高度  
  4.     BinaryTreeNode* left; // left child  
  5.     BinaryTreeNode* right; // right child  
  6. };  
  7.   
  8. // define AVL node  
  9. typedef BinaryTreeNode avlnode;  
  10.   
  11. // define AVL tree  
  12. typedef BinaryTreeNode avltree;  
  13.   
  14.   
  15. // 比较左右子树高度,取最大的  
  16. int maxh(int ha, int hb) {  
  17.     return ha > hb ? ha : hb;  
  18. }  
  19.   
  20. // 计算树的高度  
  21. int height(avltree* tree) {  
  22.     if (NULL == tree) return 0;   
  23.     return tree->height;  
  24. }  

2. AVL树旋转操作

AVL在插入和删除节点造成不平衡的时候需要对发生不平衡的节点及时调整,调整方法为旋转操作。根据造成不平衡的节点出构型可分为:LL 、RR 、LR 、RL型,对应的操作则为单旋和双旋,下面分析一下各种构型具体操作方法。

下图所示为LL构型,在B节点的左子树上插入节点导致A节点失衡,调整过程为:以B节点为轴心,A节点顺时针旋转至B的右子树,A的右子树又B的右子树代替。通过右旋操作,返回以B为Root的平衡子树。 RR够型和LL够型成对称关系,操作方向相反,此处就省略了。

LL型

下图所示为LR构型,在B节点的右子树上插入新节点导致A节点失衡,调整过程分两个步骤:首先以C为轴心,B绕C逆时针旋转,生成的子树作为A的左子树;这样就变化成了LL型,然后用上图所示的方法调整即可。通过先左旋后右旋,返回以C为Root的平衡子树。RL型和LR型呈对称状,此处也省略。

LR型


下表列出节点够型和选择操作关系。

构型操作对应的函数
LL型单旋:

右旋
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // single right rotate for LL model  
  2. avlnode* singleRightRotate(avlnode* aNode) {  
  3.     avlnode* bNode = aNode->left;  
  4.     // rebuild relation  
  5.     aNode->left = bNode->right;  
  6.     bNode->right = aNode;  
  7.     // adjust the height  
  8.     aNode->height = maxh(height(aNode->right), height(aNode->left)) + 1;  
  9.     bNode->height = maxh(height(bNode->right), height(bNode->left)) + 1;  
  10.     return bNode;  
  11. }  
RR型单旋:

左旋
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // single left rotate for RR model  
  2. avlnode* singleLeftRotate(avlnode* aNode) {  
  3.     avlnode* bNode = aNode->right;  
  4.     // rebuild relation  
  5.     aNode->right = bNode->left;  
  6.     bNode->left = aNode;  
  7.     // adjust height  
  8.     aNode->height = maxh(height(aNode->right), height(aNode->left)) + 1;  
  9.     bNode->height = maxh(height(bNode->right), height(bNode->left)) + 1;  
  10.     return bNode;  
  11. }  
LR型双旋:
先左旋
后右旋
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // double rotate for LR model  
  2. // left rotate first and then right rotate  
  3. avlnode* leftRightRotate(avlnode* aNode) {  
  4.     aNode->left = singleLeftRotate(aNode->left);  
  5.     return singleRightRotate(aNode);  
  6. }  
RL型双旋:
先右旋
后左旋
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // double rotate for RL model  
  2. // right rotate first and then left rotate  
  3. avlnode* rightLeftRotate(avlnode* aNode) {  
  4.     aNode->right = singleRightRotate(aNode->right);  
  5.     return singleLeftRotate(aNode);  
  6. }  



3. 插入节点

向AVL树中插入节点后,要判断是否引起失衡,如果失衡则需要进一步确定构型,选择合适的基本旋转操作来调整。

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // insert a key to AVL tree.  
  2. avlnode* avl_inssert(avltree* &tree, keyType key) {  
  3.     if (NULL == tree) {  
  4.         tree = (avlnode*) malloc(sizeof(avlnode));  
  5.         tree->key = key;  
  6.         tree->height = 1;  
  7.         tree->left = tree->right = NULL;  
  8.     } else if (key > tree->key) { // insert into the right subtree  
  9.         tree->right = avl_inssert(tree->right, key);  
  10.         int balanceFactor = height(tree->right) - height(tree->left);  
  11.         if (balanceFactor == 2) {  
  12.             if (key > tree->right->key) { // RR 型 , 右侧单旋  
  13.                 tree = singleLeftRotate(tree);  
  14.             } else { // RL型 , 右侧双旋  
  15.                 tree = rightLeftRotate(tree);  
  16.             }  
  17.         }  
  18.     } else if (key < tree->key) { // insert into the left subtree  
  19.         tree->left = avl_inssert(tree->left, key);  
  20.         int balanceFactor = height(tree->left) - height(tree->right);  
  21.         if (balanceFactor == 2) {  
  22.             if (key < tree->left->key) { // LL型 , 左侧单旋  
  23.                 tree = singleRightRotate(tree);  
  24.             } else { // LR型 , 左侧双旋  
  25.                 tree = rightLeftRotate(tree);  
  26.             }  
  27.         }  
  28.     } else { // if the key is already exists, nothing to do....  
  29.     }  
  30.     // 重新计算树的高度  
  31.     tree->height = maxh(height(tree->left), height(tree->right)) + 1;  
  32.   
  33.     return tree;  
  34. }  

4. 删除节点

从AVL树中删除节点分为两个步骤:首先删除节点;然后调整平衡。删除操作对应为插入操作的逆向操作,调整平衡的时候也需要确定被删除节点的分支构型来选择合适的旋转方法。

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // delete the given key from AVL tree.  
  2. avlnode* avl_delete(avltree* &tree, keyType key) {  
  3.     if (NULL == tree) {  
  4.         return NULL;  
  5.     }  
  6.     // delete the node with the given key  
  7.     if (key > tree->key) { // key exists in the right subtree  
  8.         tree->right = avl_delete(tree->right, key);  
  9.     } else if (key < tree->key) { // key exists in the left subtree  
  10.         tree->left = avl_delete(tree->left, key);  
  11.     } else {  
  12.         if (NULL != tree->left) { // when left is not NULL  
  13.             // find max node if left tree  
  14.             avlnode* dn = NULL;  
  15.             for (dn = tree->left; NULL != dn->right; dn = dn->right) {  
  16.             }  
  17.             // change the value  
  18.             tree->key = dn->key;  
  19.             // delete the max node  
  20.             tree->left = avl_delete(tree->left, dn->key);  
  21.         } else if (NULL != tree->right) { // when the right tree is not NULL  
  22.             // find the minimal node  
  23.             avlnode* dn = NULL;  
  24.             for (dn = tree->right; NULL != dn->left; dn = dn->left) {  
  25.             }  
  26.             // change the value  
  27.             tree->key = dn->key;  
  28.             // delete the minimal node  
  29.             tree->right = avl_delete(tree->right, dn->key);  
  30.         } else {            // when the node has no child  
  31.             free(tree);  
  32.             // the tree is Empty now, no need to do any operation  
  33.             return NULL;  
  34.         }  
  35.     }  
  36.     // adjust the tree to balance state after deletion  
  37.     if (height(tree->left) - height(tree->right) == 2) { // when the left subtree is too high  
  38.         if (height(tree->left->right) - height(tree->left->left) == 1) { // LR model  
  39.             tree = leftRightRotate(tree);  
  40.         } else { // LL model  
  41.             tree = singleRightRotate(tree);  
  42.         }  
  43.     } else if (height(tree->left) - height(tree->right) == -2) { // when the right subtree is too high  
  44.         if (height(tree->right->left) - height(tree->right->right) == 1) { // RL model  
  45.             tree = rightLeftRotate(tree);  
  46.         } else { // RR model  
  47.             tree = singleLeftRotate(tree);  
  48.         }  
  49.     } else {  
  50.         // the tree is already balanced, nothing to do ...  
  51.     }  
  52.   
  53.     // recalculate the height of the tree.  
  54.     tree->height = maxh(height(tree->right), height(tree->left)) + 1;  
  55.   
  56.     return tree;  
  57. }  

5. 测试

分别对 查找二叉树 和  AVL树进行下列操作:依次插入 0-15 ;依次删除 0, 3,6,10,15  

测试结果如下:

测试查找二叉树 :
依次插入 0 -15 构建如下:
前序 : 0  1  2  3  4  5  6  7  8  9  10  11  12  13  14  
中序 : 0  1  2  3  4  5  6  7  8  9  10  11  12  13  14  

依次删除 0, 3,6,10,15 之后:
前序 : 1  2  4  5  7  8  9  11  12  13  14  
中序 : 1  2  4  5  7  8  9  11  12  13  14  
测试 AVL 平衡二叉树 :
依次插入 0 -15 构建如下:
前序 : 7  3  1  0  2  5  4  6  11  9  8  10  13  12  14  
中序 : 0  1  2  3  4  5  6  7  8  9  10  11  12  13  14  

依次删除 0, 3,6,10,15 之后:
前序 : 7  2  1  5  4  11  9  8  13  12  14  
中序 : 1  2  4  5  7  8  9  11  12  13  14  

对比来看, 查找二叉树退化为线性,而AVL树则形态匀称.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。 Quartz的优势: 1、Quartz是一个任务调度框架(库),它几乎可以集成到任何应用系统中。 2、Quartz是非常灵活的,它让您能够以最“自然”的方式来编写您的项目的代码,实现您所期望的行为 3、Quartz是非常轻量级的,只需要非常少的配置 —— 它实际上可以被跳出框架来使用,如果你的需求是一些相对基本的简单的需求的话。 4、Quartz具有容错机制,并且可以在重启服务的时候持久化(”记忆”)你的定时任务,你的任务也不会丢失。 5、可以通过Quartz,封装成自己的分布式任务调度,实现强大的功能,成为自己的产品。6、有很多的互联网公司也都在使用Quartz。比如美团 Spring是一个很优秀的框架,它无缝的集成了Quartz,简单方便的让企业级应用更好的使用Quartz进行任务的调度。   课程说明:在我们的日常开发中,各种大型系统的开发少不了任务调度,简单的单机任务调度已经满足不了我们的系统需求,复杂的任务会让程序猿头疼, 所以急需一套专门的框架帮助我们去管理定时任务,并且可以在多台机器去执行我们的任务,还要可以管理我们的分布式定时任务。本课程从Quartz框架讲起,由浅到深,从使用到结构分析,再到源码分析,深入解析Quartz、Spring+Quartz,并且会讲解相关原理, 让大家充分的理解这个框架和框架的设计思想。由于互联网的复杂性,为了满足我们特定的需求,需要对Spring+Quartz进行二次开发,整个二次开发过程都会进行讲解。Spring被用在了越来越多的项目中, Quartz也被公认为是比较好用的定时器设置工具,学完这个课程后,不仅仅可以熟练掌握分布式定时任务,还可以深入理解大型框架的设计思想。
[入门数据分析的第一堂课]这是一门为数据分析小白量身打造的课程,你从网络或者公众号收集到很多关于数据分析的知识,但是它们零散不成体系,所以第一堂课首要目标是为你介绍:Ø  什么是数据分析-知其然才知其所以然Ø  为什么要学数据分析-有目标才有动力Ø  数据分析的学习路线-有方向走得更快Ø  数据分析的模型-分析之道,快速形成分析思路Ø  应用案例及场景-分析之术,掌握分析方法[哪些同学适合学习这门课程]想要转行做数据分析师的,零基础亦可工作中需要数据分析技能的,例如运营、产品等对数据分析感兴趣,想要更多了解的[你的收获]n  会为你介绍数据分析的基本情况,为你展现数据分析的全貌。让你清楚知道自己该如何在数据分析地图上行走n  会为你介绍数据分析的分析方法和模型。这部分是讲数据分析的道,只有学会底层逻辑,能够在面对问题时有自己的想法,才能够下一步采取行动n  会为你介绍数据分析的数据处理和常用分析方法。这篇是讲数据分析的术,先有道,后而用术来实现你的想法,得出最终的结论。n  会为你介绍数据分析的应用。学到这里,你对数据分析已经有了初步的认识,并通过一些案例为你展现真实的应用。[专享增值服务]1:一对一答疑         关于课程问题可以通过微信直接询问老师,获得老师的一对一答疑2:转行问题解答         在转行的过程中的相关问题都可以询问老师,可获得一对一咨询机会3:打包资料分享         15本数据分析相关的电子书,一次获得终身学习

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值