AVL树图解和Java实现
2018-03-27 13:33
771 查看
AVL树
AVL树是带有平衡条件的二叉查找树。平衡条件为:某节点左右子树的高度差不超过1。平衡操作-旋转
在AVL树的插入和删除操作中,需要更新树节点的高度信息以检查AVL树的平衡状态,如果插入和删除后出现了不平衡,还需要通过旋转操作来修正AVL树的平衡状态。插入后平衡
假设插入后非平衡节点为N,4种情况和修正方案:向N的左儿子的左子树插入:右旋
向N的右儿子的右子树插入:左旋
向N的左儿子的右子树插入:先左旋,再右旋
向N的右儿子的左子树插入:先右旋,再左旋
单旋转过程图:
上图为右旋,左旋是对称的,略去不表
从非递归编程的角度出发,基于这张图,分析要完成一次单旋转我们需要做哪些工作:
保存路径信息。在插入过程中用栈保存路径信息。
更新高度信息,检查平衡状态。插入完成后,从栈中依次弹出节点,更新其高度信息,并检查其平衡状态。如果没有遇到不平衡节点,则插入完成;如果遇到非平衡节点,继续弹出一个节点N_other代表树的其余部分。
旋转。图中是右旋。比较旋转前后的状态,大致需要五步操作完成。
3.1 确定插入方式。从非平衡节点N1出发,与新节点N3比较两次即可确定是左左插入。
3.2 周知树其余部分。更新树其余部分的儿子指向新的节点N2。
3.3 儿子移交。将N2的右子树移交给N1的左儿子。
3.4 新根上任。原N1作为N2的右儿子。
3.5 高度信息更新。观察发现旋转过程中,只有N_other,N1,N2的高度信息可能发生变化,重新为它们计算高度信息。
3.6 总结。这四步可以分为三步:1、确定插入方式2、更新三个相关节点的左右儿子信息3、更新三个节点的高度信息.
更新路径上余下节点的高度信息。
至此一次完整的左左插入过程就完成了。
双旋转过程图:
上图是先左后右双旋,先右后左是对称的,略去不表
双旋转本质上是独立的两次单旋转。
以上图为例:在确定插入方式为左右插入后,确定应该左右旋转。此时读取到非平衡节点为N1,但是对于单左旋来说,应指定其非平衡节点为N2节点。左旋完成后,再以N1节点作为非平衡节点进行右旋。
删除后平衡
AVL树和二叉查找树的删除操作情况一致,都分为三种情况,只不过AVL树在删除节点后需要重新检查平衡性并修正,同时,删除操作与插入操作后的平衡修正区别在于,插入操作后只需要对插入栈中的弹出的第一个非平衡节点进行修正,而删除操作需要修正栈中的所有非平衡节点。三种情况如下:假设被删除节点为N
N为叶子节点:直接删除
被N只有一个儿子:将父亲节点的相应儿子引用直接指向N的儿子
N有两个儿子:找到N的右子树中最小的节点N_R_MIN,该节点一定无左儿子;我们将N_R_MIN的值赋给N节点,然后删除N_R_MIN即可。(此时删除N_R_MIN节点属于前两种情况)
删除操作的大致步骤如下:
以前两种情况为基础尝试删除节点,并将访问节点入栈。
如果尝试删除成功,则依次检查栈顶节点的平衡状态,遇到非平衡节点,即进行旋转平衡,直到栈空。
如果尝试删除失败,证明是第三种情况。这时先找到被删除节点的右子树最小节点并删除它,将访问节点继续入栈。
再依次检查栈顶节点的平衡状态和修正直到栈空。
对于删除操作造成的非平衡状态的修正,可以这样理解:对左或者右子树的删除操作相当于对右或者左子树的插入操作,然后再对应上插入的四种情况选择相应的旋转就好了。
以下为代码实现:
public class AVLBalanceTree<T extends Comparable<T>> { private TreeNode<T> root; public AVLBalanceTree() { } private class TreeNode<E> { T element; int height; TreeNode<T> left; TreeNode<T> right; TreeNode(T e) { element = e; } boolean isBalanced() { int leftHeight = left == null ? -1 : left.height; int rightHeight = right == null ? -1 : right.height; return Math.abs(leftHeight - rightHeight) <= 1 ? true : false; } public String toString() { return "Element:" + element + " Height:" + height + " Balanced:" + isBalanced(); } } private int calHeight(TreeNode<T> node) { if (node == null) { return -1; } int leftHeight = node.left == null ? -1 : node.left.height; int rightHeight = node.right == null ? -1 : node.right.height; return Math.max(leftHeight, rightHeight) + 1; } // 非递归算法部分 public void insertNonRecursive(T e) { LinkedList<TreeNode<T>> trace = new LinkedList<>(); insertNonRecursive0(e, trace); reBalanceAfterInsert(trace, e); } private void reBalanceAfterInsert(LinkedList<TreeNode<T>> trace, T e) { TreeNode<T> unBalanceNode = null; TreeNode<T> unBalNodeParent = null; TreeNode<T> n = null; while ((n = trace.peek()) != null && unBalanceNode == null) { n = trace.pop(); n.height = calHeight(n); if (!n.isBalanced()) { unBalanceNode = n; } } if (trace.peek() != null) { unBalNodeParent = trace.pop(); } // 平衡状态 返回 if (unBalanceNode == null) { return; } // 确定插入方式 int compareInt = unBalanceNode.element.compareTo(e); if (compareInt > 0) { if (calHeight(unBalanceNode.left.left) - calHeight(unBalanceNode.left.right) > 0) { rotateRight(unBalanceNode, unBalNodeParent); } else { // 先左旋时,视原非平衡节点的左儿子为非平衡节点 rotateLeft(unBalanceNode.left, unBalanceNode); rotateRight(unBalanceNode, unBalNodeParent); } } else if (compareInt < 0) { if (calHeight(unBalanceNode.right.left) - calHeight(unBalanceNode.right.right) < 0) { rotateLeft(unBalanceNode, unBalNodeParent); } else { rotateRight(unBalanceNode.right, unBalanceNode); rotateLeft(unBalanceNode, unBalNodeParent); } } // 旋转完毕,旋转子树的高度信息已经更新过了,继续更新非平衡节点以上的高度信息 while ((n = trace.peek()) != null) { n = trace.pop(); n.height = calHeight(n); } } private void rotateRight(TreeNode<T> unBalanceNode, TreeNode<T> unBalNodeParent) { TreeNode<T> newRoot = unBalanceNode.left; TreeNode<T> ubParent = unBalNodeParent; // 周知树其余部分 if (ubParent != null) { if (ubParent.left != null && ubParent.left.equals(unBalanceNode)) { ubParent.left = newRoot; } else { ubParent.right = newRoot; } } else { this.root = newRoot; } // 儿子移交 if (newRoot.right != null) { unBalanceNode.left = newRoot.right; } else { unBalanceNode.left = null; } // 新根上任,左儿子无需处理 newRoot.right = unBalanceNode; // 更新三个节点的高度信息 unBalanceNode.height = calHeight(unBalanceNode); newRoot.height = calHeight(newRoot); if (ubParent != null) { ubParent.height = calHeight(ubParent); } } private void rotateLeft(TreeNode<T> unBalanceNode, TreeNode<T> unBalNodeParent) { TreeNode<T> newRoot = unBalanceNode.right; TreeNode<T> ubParent = unBalNodeParent; if (ubParent != null) { if (ubParent.left != null && ubParent.left.equals(unBalanceNode)) { ubParent.left = newRoot; } else { ubParent.right = newRoot; } } else { this.root = newRoot; } if (newRoot.left != null) { unBalanceNode.right = newRoot.left; } else { unBalanceNode.right = null; } newRoot.left = unBalanceNode; unBalanceNode.height = calHeight(unBalanceNode); newRoot.height = calHeight(newRoot); if (ubParent != null) { ubParent.height = calHeight(ubParent); } } private void insertNonRecursive0(T e, LinkedList<TreeNode<T>> trace) { TreeNode<T> node = new TreeNode<>(e); if (root == null) { root = node; root.height = 0; return; } TreeNode<T> current = root; trace.push(current); int compareInt = 0; while (true) { compareInt = current.element.compareTo(e); if (compareInt > 0) { if (current.left == null) { current.left = node; trace.push(node); return; } current = current.left; trace.push(current); } else if (compareInt < 0) { if (current.right == null) { current.right = node; trace.push(node); return; } current = current.right; trace.push(current); } else { // 相等 不插入 return; } } } public void removeNonRecursive(T e) { LinkedList<TreeNode<T>> trace = new LinkedList<>(); TreeNode<T> current = root; trace.push(current); // 尝试删除,该节点没有儿子或者只有一个儿子 if (tryRemove(e, trace, current)) { // 更新节点高度信息,修正平衡状态 // 此时栈顶为被删除节点的父节点或者空栈 if (trace.isEmpty()) { return; } // 弹出栈,更新高度信息并检查平衡 reBalanceAfterRemove(trace, e); } else { // 尝试失败,该节点有两个儿子,此时栈顶元素即为将要删除的节点A current = trace.peek(); // 找到A的右子树中最小的节点B,该节点没有左儿子 TreeNode<T> rMin = findMin(current.right); // 此时删除目标变为最小的右儿子B tryRemove(rMin.element, trace, current); reBalanceAfterRemove(trace, rMin.element); // 删除完成,将B的值替换到原来要删除的节点A上 current.element = rMin.element; } } private void reBalanceAfterRemove(LinkedList<TreeNode<T>> trace, T e) { while (true) { TreeNode<T> n = null; TreeNode<T> unBalanceNode = null; TreeNode<T> unBalNodeParent = null; while ((n = trace.peek()) != null && unBalanceNode == null) { n = trace.pop(); n.height = calHeight(n); if (!n.isBalanced()) { unBalanceNode = n; } } // 平衡态 if (unBalanceNode == null) { return; } unBalNodeParent = trace.peek(); int compareInt = unBalanceNode.element.compareTo(e); // 被删除节点是不平衡节点的左儿子,则右子树高于左子树 if (compareInt > 0) { // 如果不平衡节点的右儿子的左子树高于右子树,则需要先右后左双旋转(相当于右左插入) if (calHeight(unBalanceNode.right.left) - calHeight(unBalanceNode.right.right) > 0) { rotateRight(unBalanceNode.right, unBalanceNode); rotateLeft(unBalanceNode, unBalNodeParent); } else { // 相等和小于的情况 都可以通过左旋平衡 rotateLeft(unBalanceNode, unBalNodeParent); } } else { // 相当于左右插入 if (calHeight(unBalanceNode.left.left) - calHeight(unBalanceNode.left.right) < 0) { rotateLeft(unBalanceNode.left, unBalanceNode); rotateRight(unBalanceNode, unBalNodeParent); } else { // 相等和大于的情况 都可以通过右旋平衡 rotateRight(unBalanceNode, unBalNodeParent); } } for (TreeNode<T> n1 : trace) { n1.height = calHeight(n1); } } } private boolean tryRemove(T e, LinkedList<TreeNode<T>> trace, TreeNode<T> current) { while (true) { int compareInt = current.element.compareTo(e); if (compareInt > 0) { if (current.left == null) { // 没有找到节点 直接返回 return true; } current = current.left; trace.push(current); } else if (compareInt < 0) { if (current.right == null) { // 没有找到节点 return true; } current = current.right; trace.push(current); } else { // 找到元素,情况一:该节点为叶子节点 if (current.left == null && current.right == null) { // 弹出被删除节点 trace.pop(); if (current == root) { root = null; return true; } TreeNode<T> parent = trace.peek(); // 查询父节点 if (parent.left == current) { parent.left = null; return true; } else { parent.right = null; return true; } } // 找到元素,情况二:该节点只有一个儿子 if (current.left == null) { trace.pop();// 弹出被删除节点 TreeNode<T> son = current.right; if (current == root) { root = son; return true; } TreeNode<T> parent = trace.peek(); if (parent.left == current) { parent.left = son; return true; } else { parent.right = son; return true; } } if (current.right == null) { trace.pop();// 弹出被删除节点 TreeNode<T> son = current.left; if (current == root) { root = son; return true; } TreeNode<T> parent = trace.peek(); if (parent.left == current) { parent.left = son; return true; } else { parent.right = son; return true; } } // 找到节点,情况三:但是节点有两个儿子,返回false return false; } } } }
相关文章推荐
- AVL树图解和Java实现
- AVL树图解和Java实现
- AVL树图解和Java实现
- AVL树图解和Java实现
- AVL树图解和Java实现
- 数据结构--AVL树的insert()的Java实现
- AVL树的插入与删除---Java实现
- AVL树的JAVA实现及AVL树的旋转算法
- java各种算法排序图解以及原码实现
- 数据结构学习之用Java实现AVL树
- java实现AVL树(一种自平衡二叉树)数据结构
- AVL树(三)之 Java的实现【转】
- 算法 -- Java实现选择排序(图解 + 代码实现)
- AVL树原理及实现(C语言实现以及Java语言实现)
- 算法 -- Java实现快速排序(图解 + 代码实现)
- java数据结构与算法之平衡二叉树(AVL树)的设计与实现
- java实现AVL树
- Java调用c,c++图解(VS2012实现类库)
- java实现选择排序和冒泡排序及执行流程图解
- AVL树(三)之 Java的实现