前文介紹了,二叉樹、二叉排序樹,需要了解的不妨關(guān)注下小JIA。
AVL是一種高度平衡的二叉排序樹。對于任意節(jié)點左子樹與右子樹高度差不超過1,AVL的高度與節(jié)點數(shù)量為O(logn)關(guān)系。平衡因子等于左子樹高度減去右子樹高度。AVL所有節(jié)點的平衡因子只可能是-1、0、1。因此當(dāng)添加元素或刪除元素時有可能會破會這種平衡,所以需要維護。失去平衡主要有四種情況,分別為LL、LR、RR和RL。
AVL 的節(jié)點定義
public class AVLTreeNode<T extends Comparable<T>> {
private T key; //關(guān)鍵字(鍵值)
private int height; //高度
private AVLTreeNode<T> left; //左孩子
private AVLTreeNode<T> right; //右孩子
public AVLTreeNode(T key, AVLTreeNode<T> left, AVLTreeNode<T> right) {
this.key = key;
this.left = left;
this.right = right;
this.height = 0;
}
......
}
LL
LL,平衡因子大于1,左子樹平衡因子大于等于0,需要將A順時針向下右旋轉(zhuǎn),B做為父節(jié)點
右旋轉(zhuǎn)操作,首先保存B右子樹K3,將A做為B的右子樹,K3做為A的左孩子,并更新A,B的高度值,完成旋轉(zhuǎn)。
/* LL:左旋轉(zhuǎn) */
private AVLTreeNode<T> leftLeftRotation(AVLTreeNode<T> node) {
AVLTreeNode<T> tempNode;
tempNode = node.getLeft();
node.setLeft(tempNode.getRight());
tempNode.setRight(node);
node.setHeight(max(height(node.getLeft()), height(node.getRight())) + 1);
tempNode.setHeight(max(height(tempNode.getLeft()), node.getHeight()) + 1);
return tempNode;
}
RR
RR,平衡因子小于-1,右子樹平衡因子小于等于0,需要將A逆時針向下左旋轉(zhuǎn),B做為父節(jié)點
左旋轉(zhuǎn)操作,首先保存B左子樹K2,將A做為B的左子樹,K2做為B的右孩子,并更新A、B的高度值,完成旋轉(zhuǎn)
/* RR:右旋轉(zhuǎn) */
private AVLTreeNode<T> rightRightRotation(AVLTreeNode<T> node) {
AVLTreeNode<T> tempNode;
tempNode = node.getRight();
node.setRight(tempNode.getLeft());
tempNode.setLeft(node);
node.setHeight(max( height(node), height(node.getRight())) + 1);
tempNode.setHeight(max( height(tempNode.getRight()), node.getHeight()) + 1);
return tempNode;
}
LR
LR,平衡因子大于1,左子樹平衡因子小于0,首先將B進行左旋轉(zhuǎn),在將A進行右旋轉(zhuǎn)
/* LR:左雙旋轉(zhuǎn) */
private AVLTreeNode<T> leftRightRotation(AVLTreeNode<T> node) {
node.setLeft(rightRightRotation(node.getLeft()));
return leftLeftRotation(node);
}
RL
RL,平衡因子大于-1,右子樹平衡因子大于0,首先將B進行右旋轉(zhuǎn),在將A進行左旋轉(zhuǎn)
/* RL:右雙旋轉(zhuǎn) */
private AVLTreeNode<T> rightLeftRotation(AVLTreeNode<T> node) {
node.setRight(leftLeftRotation(node.getRight()));
return rightRightRotation(node);
}
插入節(jié)點
原則:根據(jù)二叉查找樹BST的規(guī)定插入數(shù)據(jù),再來判斷是否失去平衡;若失去平衡再根據(jù)文上介紹的旋轉(zhuǎn)規(guī)則平衡數(shù)據(jù);最后再設(shè)置樹高。
/* 將結(jié)點插入到AVL樹中 */
private AVLTreeNode<T> insert(AVLTreeNode<T> tree, T key) {
if (tree == null) {
//新建節(jié)點
tree = new AVLTreeNode<T>(key, null, null);
} else {
int cmp = key.compareTo(tree.getKey());
if (cmp < 0) { //將key插入到tree的左子樹
tree.setLeft(insert(tree.getLeft(), key));
//插入節(jié)點后,若AVL樹失去平衡,則進行相應(yīng)的調(diào)節(jié)。
if (height(tree.getLeft()) - height(tree.getRight()) > 1) {
if (key.compareTo(tree.getLeft().getKey()) < 0)
tree = leftLeftRotation(tree);
else
tree = leftRightRotation(tree);
}
} else if (cmp > 0) {//將key插入到tree的右子樹
tree.setRight(insert(tree.getRight(), key));
//插入節(jié)點后,若AVL樹失去平衡,則進行相應(yīng)的調(diào)節(jié)。
if (height(tree.getRight()) - height(tree.getLeft()) > 1) {
if (key.compareTo(tree.getRight().getKey()) > 0)
tree = rightRightRotation(tree);
else
tree = rightLeftRotation(tree);
}
}
}
tree.setHeight(max(height(tree.getLeft()), height(tree.getRight())) + 1);
return tree;
}
刪除節(jié)點
同理,先找到刪除節(jié)點的位置,再進行AVL平衡調(diào)節(jié)。找到要刪除的節(jié)點Z后,如果Z的左右孩子都非空,
a)若Z的左子樹高于右子樹,找出左子樹中的最大節(jié)點K(maxNum方法),使用K的值替換掉Z的值,并刪除K;
b)若Z的左子樹矮于或等于右子樹,找出右子樹中最小節(jié)點K(minNum方法),使用K的值替換掉Z的值,并刪除K。
這種方式的好處是刪除后AVL樹仍然是平衡的。
/* 刪除結(jié)點 */
private AVLTreeNode<T> remove(AVLTreeNode<T> tree, AVLTreeNode<T> delNode) {
//根為空 或者 沒有要刪除的節(jié)點,直接返回null。
if (tree == null || delNode == null)
return null;
int cmp = delNode.getKey().compareTo(tree.getKey());
if (cmp < 0) { //待刪除的節(jié)點在tree的左子樹中
tree.setLeft(remove(tree.getLeft(), delNode));
// 刪除節(jié)點后,若AVL樹失去平衡,則進行相應(yīng)的調(diào)節(jié)。
if (height(tree.getRight()) - height(tree.getLeft()) > 1) {
AVLTreeNode<T> r = tree.getRight();
if (height(r.getLeft()) > height(r.getRight()))
tree = rightLeftRotation(tree);
else
tree = rightRightRotation(tree);
}
} else if (cmp > 0) { //待刪除的節(jié)點在tree的右子樹中
tree.setRight(remove(tree.getRight(), delNode));
// 刪除節(jié)點后,若AVL樹失去平衡,則進行相應(yīng)的調(diào)節(jié)。
if (height(tree.getLeft()) - height(tree.getRight()) > 1) {
AVLTreeNode<T> l = tree.getLeft();
if (height(l.getRight()) > height(l.getLeft()))
tree = leftRightRotation(tree);
else
tree = leftLeftRotation(tree);
}
} else { // tree是對應(yīng)要刪除的節(jié)點。
// tree的左右孩子都非空
if ((tree.getLeft() != null) && (tree.getRight() != null)) {
//如果tree的左子樹比右子樹高;
if (height(tree.getLeft()) > height(tree.getRight())) {
AVLTreeNode<T> max = maxNum(tree.getLeft());
tree.setKey(max.getKey());
tree.setLeft(remove(tree.getLeft(), max));
//如果tree的左子樹矮于或等于右子樹
} else {
AVLTreeNode<T> min = minNum(tree.getRight());
tree.setKey(min.getKey());
tree.setRight(remove(tree.getRight(), min));
}
} else {
AVLTreeNode<T> tmpNode = tree;
tree = (tree.getLeft() != null) ? tree.getLeft() : tree.getRight();
tmpNode = null;
}
}
return tree;
}






