Java实现二叉树(二):平衡二叉树的实现之AVL树
前文中,我们实现了二叉查找树,同时,我们也提到了这么一个隐患:如果二叉树无法控制自己的深度,那么,二叉树的查找效率很可能会发生极端的转化--如,顺序的将一堆数据插入查找二叉树,此时,二叉树会成为一个近似链表的数据结构。所以,为了解决这个问题,我们必须要找到问题的根源所在--二叉树深度,由此,推出本文的主角--平衡二叉树。
一、平衡二叉树的概念
定义:平衡二叉树,要求左右子树的深度差别不超过1,且左右子树都是平衡二叉树。(二叉树的定义总是充满了递归味)
平衡二叉树的实现方式:平衡二叉树有多种实现方式,最常见的就有AVL树以及红黑二叉树,AVL树是最早被发明的平衡二叉树,敬它是前辈,本文先说AVL树的实现。
二、AVL树的概念
定义在上头已经说过,咱们直接看看AVL树如何实现自平衡的吧(所谓自平衡,就是在节点发生修改的时候,自己完成树平衡的调整)。
示例:
假设有如下四种树:
要说不平衡的情况其实千变万化,比如左边深度为一万,右边深度为0,但是我们考虑的是,在平衡树中修改一个节点以后,发生的不平衡“事件”,所以,左右的差只能为2。
- 第一种情况,是根节点的左子树高于根节点的右子树,且,左子树的非叶子节点为左子树,此情况称之为“左左”。
解决方案:将左子树的叶子节点--也就是他的右子树,“剪切”到根节点的左边,成为根节点的左叶子,因为他满足大于左子树且小于根节点的要求。再将原左子树变为根节点,根节点变为新右子树,平衡的同时,满足了二叉查找树的要求。
实现步骤: - 第二种情况,对应着图4,属于情况一的对称,就不赘述了。
- 第三种情况,对应图2,同样是,根节点的左子树高于根节点的右子树,但是左子树暴露出来的叶子节点十分不友好,因为他是二叉树的左叶子,此时称之为“左右”,左叶子虽然满足小于根节点的要求,但是不大于左子树,所以无法直接“剪切”。
解决方法:将左子树完成一次逆时针右旋转,再将根节点完成一次顺时针左旋转。
实现步骤: - 第四情况,对应图3,同样对称,也就不说了。
总结:其实旋转的本质,是让不平衡的更深一端,替换根节点,降低其深度,这时,更深端的子树就变成原根节点,如何处理原更深端的子树,就成了最大问题。如左左,右右,都是有一个能符合要求的叶子节点,能直接完成叶子的剪切。左右和右左,暴露出来的叶子都不能完成剪切,所以需要对(左或者右)节点来一次旋转,让上层的树有一个符合要求的叶子。