接上章:二叉搜索树(Binary Search Tree).
通过二叉搜索树的学习,我们发现,二叉搜索树节点的添加和删除都是随机的,这里就会产生一种特殊情况,如下图所示:
此种情况,是从小到达的添加各个节点,此时二叉搜索树已经退化为链表了。或者删除节点,也可能会导致搜查搜索树退化成链表。那么,如何防止二叉搜索树退化成链表呢?
因为二叉搜索树的复杂度在O(logn),所以在添加、删除二叉搜索树节点的时候复杂度一直维持在O(logn)即可。这里有一个平衡二叉搜索树的概念。
平衡:当节点数量固定时,左右子树的高度越接近,这棵二叉树就越平衡(高度越低)。
西方有一句民谣是这样说的:“丢失一个钉子,坏了一只蹄铁;坏了一只蹄铁,折了一匹战马;折了一匹战马,上了一位骑士;上了一位骑士,输了一场战斗;输了一场战斗,亡了一个帝国。”所以,所谓平衡二叉树,其实就是在二叉树添加删除的过程中保证它的平衡性,一旦发自按有不平衡的情况,马上处理恢复平衡,这样就不会造成不可收拾的情况出现了。
也就是说,在添加、删除操作之后,想办法让二叉搜索树恢复平衡(减小输的高度)即可。
经典常见的平衡二叉搜索树也就是我们今天所说的AVL树。
一、【AVL树】性质:
◼ 平衡因子(Balance Factor):某结点的左右子树的高度差
◼ AVL树的特点
◼ 每个节点的平衡因子只可能是 1、0、-1(绝对值 ≤ 1,如果超过 1,称之为“失衡”)
◼每个节点的左右子树高度差不超过 1
◼搜索、添加、删除的时间复杂度是 O(logn)
添加、删除操作均会导致AVL树失衡
例:我们先来看一个平衡二叉树构建的例子。假设我们有如下一个数组a[10]= {3,2,1,4,5,6,7,10,9,8},需要构建二叉搜索树。在没有学习AVL树之前,根据二叉搜索树的特性,我们通常会将它构建成如图003-图1所示。
虽然它完全符合二叉搜索树的性质,但是对于这样高度达到8的二叉搜索树来首,查找是非常不利的。我们更期望能够构建成如图003-图2所示的样子,高度为4的二叉搜索树才可以提高查找的效率。那么现在我们来看一下如何构建出如图003-图2所示的树结构。
二、【AVL树】节点的增加和删除
1.节点添加步骤:
我们根据数组a[10]= {3,2,1,4,5,6,7,10,9,8},来一个个添加元素.
◼1. 前两位【3】和【2】我们都很正常的构建,到了第三个数字节点
【1】
的时候,发现此时根节点【3】的平衡因子变成了2,此时整棵树都成了最小的不平衡子树,因此需要调整。所以需要将整个树进行右旋转(顺时针旋转),此时节点【2】成了根节点,【3】成为【2】的右孩子,这样三个节点的平衡因子值均为0,非常的平衡,完成了平衡二叉树的调整。如下图所示:
◼2. 接着添加节点
【4】
,平衡因子此时没发生改变,如图005所示
◼3. 接着添加节点
【5】
,节点【3】的平衡因子值为-2,说明要旋转了。所以我们对这棵最小平衡子树进行左旋(逆时针旋转),如图006所示,此时整个树又达到了平衡。
◼ 4. 继续添加节点
【6】
,发现根节点【2】的平衡因子变成了-2,如图007-图1所示。所以需要对根节点进行左旋转,注意此时本来根节点【3】是【4】的左孩子,由于旋转后需要满足二叉搜索树的性质,所以它成了节点【2】的右孩子。如图007-图3所示。
◼5. 继续添加节点【7】 ,发现节点【5】的平衡因子变成了-2,所以同样的左旋转,使得整棵树达到平衡,如图008所示。
◼6. 继续添加节点【10】,结构无变化,如图009所示。
◼7. 继续再添加节点【9】,此时节点【7】的平衡因子变成了-2,因为节点【9】是父节点【10】的左孩子,由于旋转后需要满足二叉搜索树的性质,所以这里需要进行两次旋转。
7.1 先对节点【9】和节点【10】进行右旋转,使得节点10成了9的右子树。
7.2 再以节点【7】为最小不平衡子树进行左旋转。
旋转过程如图010所示:
中间转换过程放大图如下:
◼ 8. 最后添加节点【8】,情况与刚刚类似,需要进行两次旋转。
8.1 先以节点【9】为根节点,进行右旋;
8.2 再以节点【6】为根节点,进行左旋。右旋转过程如图011所示:
右旋中间转换过程放大图:
左旋转过程如图012所示:
左旋中间转换过程放大图:
通过这个例子,我们可以总结出来,AVL树的旋转一共分为四种情况,分别是LL,RR,LR,RL。
这里我们定义一个二叉搜索树P
左子节点定义为L,
右子节点定义为R,
三角形代表子树,
N定义为新增节点
情况一:LL-右旋转(单旋)
◼ ① L.right = P.left;
◼ ② P = L.right
◼ ③ 让L成为这棵树的根节点
◼ ④ 整棵树达到了平衡
注意:(1).LR、L、P的parent;(2).先后更新P、L的高度。
情况二:RR-左旋转(单旋)
◼ ① R.left = P.right;
◼ ② P = R.left
◼ ③ 让R成为这棵树的根节点
◼ ④ 整棵树达到了平衡
注意:(1).RL、R、P的parent;(2).先后更新P、L的高度。
情况三:LR-左右旋转(双旋)
◼ 只需要先左
旋转,使平衡因子BF值符号统一,然后再进行右
旋转。
情况四:RL-右左旋转(双旋)
◼ 只需要先右
旋转,使平衡因子BF值符号统一,然后再进行左
旋转。
2.节点删除步骤:
添加节点可能导致AVL树失衡,那么节点也可能导致AVL树失衡。如下图所示,删除节点后平衡因子如下:
◼ 删除子树中的 16
◼ 可能导致了父节点15失衡了。这里属于LL的情况,所以进行左旋转,调整后如下所示:
好了,AVL树的学习先暂且到这里,后序如果有更深入的了解,继续补充学习。