平衡二叉树(AVL树)
为什么要在二叉搜索树的基础上提出平衡二叉树?
考虑这样一种情况:
当我们的二叉搜索树结构如图所示时,
这棵树与单链表的查找,删除时间复杂度相比相差无几,甚至高于单链表(二叉搜索树还需要判断左子树)
为了避免上述不平衡的存储结构出现
以及保证较高的查询效率,提出了平衡二叉树
平衡二叉树定义:任意节点的子树的高度差都小于等于1
基于Java实现平衡二叉搜索树(AVL树)
为了减少文章篇幅,本文基于上篇博客的二叉树代码完成AVL树的构建,并主要讲解平衡二叉树的旋转问题。
关于二叉排序树的详细构建,代码见:二叉排序树
首先实现getHeight方法得到当前树的高度和其子树的高度:
//返回根结点的高度 public int height(){ return Math.max(left == null ? 0 : left.height(),right == null ? 0 : right.height())+1; }
递归思想解释:以根结点左结点和右节点为初始递归条件,一直往下递归到叶子结点,往上每返回一层递归函数高度加一,比较左右结点路径的深度,得到最大深度
获得当前结点的左子结点和右子结点深度的思想类似:
//返回左子树的高度 public int LeftHight(){ if(left == null){ return 0; }else{ return left.height(); } }
//返回右子树的高度 public int rightHeight() { if(right==null){ return 0; }else{ return right.height(); } }
接着考虑一般情况下的左旋和右旋情形:
左旋:该树满足所有结点的右侧树高度均大于左侧树高度
由于右边比左边高,所以很容易想到将根节点从5移动到8,对结点8周围的三条连接线做处理,为了满足二叉树的定义,
将6接到结点5上。而此时如果为了得到高度相同的左右子树而直接修改根节点,会涉及到大量的修改。
所以为了方便起见,在右树中删除一个结点,在左树中添加一个结点
左旋代码实现:
public void leftRotate(){ //创建新的结点存储信息 Node newNode = new Node(value); newNode.left = left; newNode.right = right.left; value = right.value; right = right.right; left = newNode; }
单次右旋思路一致:所有结点的左子树高度大于其右子树高度即单次右旋
public void rightRotate(){ Node newNode = new Node(value); newNode.right = right; newNode.left = left.right; value = left.value; left = left.left; right = newNode; }
接着考虑双旋转问题的解决:
此时通过单次左旋无法使树恢复平衡状态,可以单看结点11,结点11的左子树高度为2,右子树为0,刚好与整体树失衡的方向相反,
那么我们对结点11做一次右旋再对树整体做一次左旋即可完成平衡二叉树的构建。
if(rightHeight()-LeftHight() > 1){ if(left != null && left.LeftHight() > left.rightHeight()){ left.rightRotate(); leftRotate(); }else { leftRotate(); } }
对以上方法进行总结,并将其添加到add方法中:因为每添加一个新数据就需要判断树当前是否失衡。
整体add方法如下:
public void add(Node node){ if(node == null){ return; } if(this.value > node.value){ if(this.left == null){ this.left = node; }else{ this.left.add(node); } }else{ if(this.right == null){ this.right = node; }else{ this.right.add(node); } } //右旋 if(LeftHight()-rightHeight() > 1){ if(right != null && left.rightHeight() > left.LeftHight()){ left.leftRotate(); rightRotate(); }else { rightRotate(); } return; } //左旋 if(rightHeight()-LeftHight() > 1){ if(left != null && left.LeftHight() > left.rightHeight()){ left.rightRotate(); leftRotate(); }else { leftRotate(); } } }