您的位置:首页 > 编程语言 > Java开发

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;
}
}

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息