目录
前言:
在C++中,AVL树是在二叉搜索树的基础上优化而来的,因为二叉搜索树有一个缺陷,即如果插入的数据接近有序或者有序,那么二叉搜索树就会变成一边倒的结构,也就是”单支树“,而”单支树“查找数据效率就会变成和线性数据结构一样的O(N),失去了二叉树本有的优势。 单支树示意图如下:
因此两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年推出了AVL树的概念,AVL树在每一次插入新节点的同时,会自动调整该树的高度,即不会让每个节点的左右子树高度的绝对值不超过1,因此哪怕面对有序数据的插入也不会让树变成”单支树“,可以使查找数据的效率始终保持在O(log N)。
1、AVL的概念
AVL树满足以下两个条件:
1、其每颗子树都是AVL树。
2、每个节点的左右子树高度(可在节点中新加一个变量-平衡因子表示该节点的左右子树高度)的绝对值不超过1。
AVL树示意图如下:
其中,-1表示该节点的左子树高度高于右子树高度1个单位,0表示该节点左右子树高度一样,1表示该节点的右子树高度高于左子树高度1个单位。转换成公式:平衡因子=右子树高度-左子树高度。
2、平衡因子的调整概念
AVL树插入新节点的规则依旧是按照二叉搜索树的规则,即左节点小于根结点,而右节点大于跟节点。只不过AVL新加入了平衡因子的概念,所以每次插入节点后,都需要对平衡因子做出调整,比如插入的节点为该子树的根结点的右边,则根结点的平衡因子需要+1。
此时,平衡因子的状态就会出现三种情况:
1、插入节点后,根结点的平衡因子从正负1变为0,这种情况说明插入该节点后这棵树子树得到了平衡,无需做任何处理。
2、插入节点后,根结点的平衡因子从0变为1或-1,这种情况说明插入的节点破坏了该树的原有平衡,虽然当前子树的根结点的平衡因子的绝对值没有超过1,但是该子树的祖先节点的平衡因子的绝对值可能已经超过1了,所以需要”向上更新“,更改祖先节点的平衡因子。
3、插入节点后,该树的某个节点的平衡因子的绝对值超过了1,则需要对该子树进行旋转处理。
具体示意图如下:
此时会发现,不管新插入的节点在9的右边还是左边,都会导致节点8的平衡因子变成2,因为对于9来说可能在哪边插入节点都行,但是对于节点8来说,这两个插入的节点都在8的右子树,所以会导致8的平衡因子变成2。
在节点9的左边插入节点的情况如下:
只有在8的左边插入节点,才不会引发旋转调整,示意图如下:
3、AVL树的插入
AVL树的插入函数的实现可以分成三步:1、找到插入节点的合适位置。2、调整节点的平衡因子。3、对平衡因子异常的节点进行旋转操作。
第一步是复用了二叉搜索树的插入逻辑,即判断要插入节点的值是否大于或小于根结点,若大于根结点则往右子树遍历,若小于根结点则往左子树遍历,直到找到节点