上节讨论的DSW算法能够从全局从新平衡树:每一个节点均可能参与树的从新平衡,或者从节点中移除数据,或者从新设置指针的值。可是,当插入或删除元素时,将只影响树的一部分,此时树的从新平衡能够只在局部进行。局部平衡二叉查找树有一个鼎鼎大名的算法,叫作AVL算法,由AVL算法构建出来的二叉树称之为AVL树。
AVL树首先是一颗二叉查找树,其次,AVL树要求每一个节点左右子树的高度差最大为1。例如,图1-1中的全部树都是AVL树。节点中的数字表示平衡因子,即左右子树的高度差。平衡因子等于右子树的高度减去左子树的高度。对于AVL树,全部的平衡因子都应是+一、0或-1。平衡AVL树的技术不保证获得的树是彻底平衡的,但倒是高度平衡的。有关平衡的概念能够参考数据结构与算法-二叉查找树平衡(DSW)。
假设咱们已经有了一颗AVL树,那么如何保证在插入或者删除元素时继续保持AVL树的特性呢?
前人对于AVL树的插入操做进行总结,发现一共有4种状况须要讨论。其中,在左子树中插入有两种状况,在右子树中插入也有两种状况。而且它们是对称的,所以,咱们以右子树为例,只须要讨论两种状况便可。
假设咱们在AVL树中插入一个新的节点p,那么p的父节点以及祖父节点的平衡因子都有可能改变,咱们从下到上更新节点的平衡因子,若是发现某个节点的平衡因子成为+2或者-2,那么就以该节点为根截取这段子树。若是根节点平衡因子成为了+2,表明节点插入到了右子树中,若是根节点平衡因子成为了-2,表明节点插入到了左子树。咱们这里只讨论+2,即节点插入到右子树上的两种状况。
若是节点平衡因子变成了+2,那么,咱们能够将这颗子树抽象出来,它必然是下面这种样子:
也就是说,若是插入新节点使P节点的平衡因子成了+2,那么以P为根的子树,必然是上面的样子。插入的新节点要么放在Q的左子树上,要么放在Q的右子树上,这就是咱们要讨论的两种状况。
第一种状况有个简称,叫作RR,也就是说新节点插入到右子树的右节点,就像这样:
怎么处理呢?P节点的左子树比右子树低,还记得左旋的效果吗?左旋能够提升左子树的高度,下降右子树的高度,用在这里正合适。将Q节点围绕P节点进行左旋以后效果以下:
很是完美,左旋在这里产生了两个效果。首先,左旋使当前子树从新保持平衡,其次,左旋没有改变当前子树的高度。左旋以前子树高度为h + 2,左旋以后依然是h + 2。这就意味着,从Q节点开始往上的节点不须要更新平衡因子了。因此,左旋使得算法变得足够简单。
顾名思义,RL的意思是将新节点插入到右子树的左节点上,就像这样:
怎么处理呢?首先仍是左旋,可是很快你会发现,左旋确实提升了左子树高度,可是过高了,又形成了新的不平衡,就像这样:
究其缘由,是h + 1那颗子树大大提升了左子树高度,咱们必须把它放在右子树上才行,能够这样处理。首先,咱们认为,若是是由于在P树的右子树的左节点上插入元素致使P节点不平衡,那么,在没有插入新节点以前,P树确定是下面这种样子的:
Q的左子树插入了新的节点,可能插入到R的左子树或者右子树,先无论这个,为了下降Q的左子树的高度,将R围绕着Q进行右旋,结果是这样:
靠谱,R的左子树高度就是就是m1树的高度,既多是h - 1,也多是h,可是都比h + 1要小,这时咱们再作一次左旋,结果以下:
完美,R树最终平衡了,而且R树的高度依然保持在h + 2,这也意味着没必要更新R节点之上的祖先节点的平衡因子了。至于P和Q节点的平衡因子,这要看新节点是插入到m1树仍是m2树上了,可是不管如何,R树都已经高度平衡了。
总结一下,对于RL形式的子树,首先对右子树的左节点作一次右旋,而后对新的根节点作一次左旋便可。
插入还有LL,LR两种形式,可是它们分别和RR,RL形式对称,这里就很少介绍了。
删除比插入更加复杂,可是有着插入节点积累的经验,理解删除反而更加容易。
删除节点首先面对的问题是平衡因子从哪一个节点开始变化的?这就要说到二叉查找树的删除逻辑了,在数据结构与算法-二叉查找树中有着详细的介绍。这里简单介绍一下,删除叶子节点或者度为1的节点,平衡因子从被删除节点的父节点开始变化。删除度为2的节点须要使用复制删除的技巧,使用被删除节点的直接前驱或者直接后继来替换被删除节点,而后删除直接前驱或者直接后继,那么,平衡因子就是从直接前驱或者直接后继的父节点开始变化的。
既然知道了平衡因子开始变化的节点,就能够从下到上的更新平衡因子的变化。
在探讨插入节点引发的二叉树的变化时,咱们总结出了RR、RL、LL、LR四种状况,删除节点会引发怎样的变化呢?
一、删除不只包含以上四中变化,还多出两种,固然这两种也是对称的
多出来的两种是当前节点的父节点的平衡因子等于+2或者-2时,当前节点的平衡因子等于0。
插入节点之因此没有这两种变化,是由于插入节点使当前节点平衡因子变成0,说明当前节点的高度没有变化,更加不会引发祖先节点的平衡因子变化了。而删除节点时,若是当前节点平衡因子变成0,那么当前节点的高度其实变小了,可能引发祖先节点的平衡因子变化。
怎么处理这种状况呢?若是父节点平衡因子等于+2,那就对当前节点进行左旋。若是父节点平衡因子等于-2,那就对当前节点进行右旋便可。
二、从新平衡过程没有在发现平衡因子为+2或者-2的第一个节点P后中止,而是追踪到根节点
在插入节点的从新平衡过程当中,咱们在乎的是第一个平衡因子为+2或者-2的节点,缘由在于咱们执行左旋或者右旋以后,子树不只从新平衡而且高度也和没有插入节点以前保持一致,所以,祖先节点的平衡因子就不会发生变化了。可是删除节点发生了变化,从新平衡以后子树的高度变小了。
为何从新平衡以后,插入节点高度不变而删除节点高度变小呢?
能够思考下平衡的过程,假设平衡因子变化为+2的节点为P,它的左子树高度为h,右子树高度必然为h + 1,只有在右子树插入新的节点,P节点的平衡因子才会变化为+2,从新平衡以后该树的左右子树高度为h + 1,所以该树的高度h + 2没有变化。可是对于删除又有不一样,假设P节点左子树高度为h,右子树高度必然为h + 1,只有在左子树删除节点以后,P节点的平衡因子才会变化为2,从新平衡以后该树的左右子树的高度为h,那么该树的高度h + 1比原来就减小了1。由于上述缘由,删除节点以后从新平衡的逻辑不能只执行一次,而是从平衡因子发生变化的节点开始往上一直追踪到根节点,发现不平衡的节点都要执行平衡逻辑。
到此为止,AVL树中添加和删除节点的逻辑已经探讨完毕。固然,这里纯粹是学术上的探讨,没有代码示例,更加精深的内容须要在实践中摸索。
知识的积累历来不是看了一两篇文章就能够完成的,毕竟,功夫在诗外。