AVL樹是帶有平衡條件的二叉查找樹,它允許每個節點的左子樹與右子樹的高度差未1。這樣的平衡樹深度是O(log N)。而要維持這種平衡,就必須在每次對AVL樹刪除節點或新增節點後,檢查AVL樹的平衡是否被打破,即是否存在節點的左右子樹高度差大於1,在判斷AVL樹失去平衡後,就必須旋轉失去平衡的節點,已再次達到平衡。
AVL樹旋轉:
旋轉的目的就是減小樹的高度,節點未空時,節點的高度未-1;
1.單旋轉:
當在節點3的插入節點2前,節點5的高度爲1,左節點3的高度爲0,插入節點2後,節點5左節點3的高度爲1,而右節點高度爲-1,兩者高度差等於2,節點5失去平衡。
旋轉完成後,AVL樹的高度降低,需重新計算每個節點的高度(遞歸計算)。
左旋:
計算節點高度同上,計算後節點2失去平衡,將節點2旋轉到其右節點 3的左節點上,在遞歸計算AVL樹每個節點的高度。
2.雙旋轉:
先左旋再右旋:
如同所示,在節點3上插入節點4後,節點5失去平衡,如果旋轉恢復平衡則要先將節點3旋轉至節點4的左節點上,再Jiangxi節點5旋轉至節點4的右節點上,通過左旋+右旋兩次旋轉恢復AVL樹的平衡,並重新計算節點高度。
先右旋再左旋:
如圖所示,在節點5插入節點4後,AVL樹在節點3處失去平衡,因此先將節點5右旋至節點4的右節點上,在講節點3旋轉至節點4的左節點上,通過右旋+左旋兩次旋轉恢復AVL樹的平衡,並重新計算節點高度。
AVL樹的添加及刪除操作代碼如下:
package com.ybin.btree;
/**
* @author yuebing
* @version 1.0
* @Date 2018/5/11
* @category AVL樹操作
*/
public class AvlTree<E extends Comparable> {
/**
* 根節點
*/
private Node root;
/**
* AVL樹高度
*/
private int height;
private static final class Node<E> {
/**
* 元素值
*/
E element;
/**
* 左節點
*/
Node left;
/**
* 右節點
*/
Node right;
/**
* 節點高度
*/
int nodeHeight;
public Node(E element) {
this(element, null, null);
}
public Node(E element, Node left, Node right) {
this.element = element;
this.left = left;
this.right = right;
}
}
public void add(E e) {
if (e != null)
this.root = insert(e, root);
}
private Node insert (E e, Node node) {
if (node == null) {
return new Node(e);
}
int compare = e.compareTo(node.element);
if (compare < 0) {
node.left = insert(e, node.left);
}else if (compare > 0) {
node.right = insert(e, node.right);
} else {
return node;
}
return balance(node);
}
public void remove(E e) {
if (e != null) {
this.root = this.remove(e, root);
}
}
private Node remove(E e, Node node) {
if (node == null) {
return node;
}
int compare = e.compareTo(node.element);
if (compare < 0) {
node.left = remove(e, node.left);
} else if (compare > 0) {
node.right = remove(e, node.right);
} else if (node.left != null && node.right != null) {
//找出該節點右節點的最小節點,替換被刪除的節點
Node min = node.right;
while (min.left != null) {
min = min.left;
}
node.element = min.element;
node.right = remove((E) node.element, node.right);
} else {
node = node.left == null ? node.right : node.left;
}
return this.balance(node);
}
/**
* 添加節點後,平衡AVL樹
*
* @param node
* @return
*/
private Node balance(Node node) {
if (node == null) {
return node;
}
if (height(node.left) - height(node.right) > 1) {
if (height(node.left.left) >= height(node.left.right)) {
node = this.rightRotate(node);
} else {
node = this.leftAndRightRotate(node);
}
} else if (height(node.right) - height(node.left) > 1) {
if (height(node.right.right) >= height(node.right.left)) {
node = this.leftRotate(node);
} else {
node = this.rightAndLeftRotate(node);
}
}
node.nodeHeight = Math.max(height(node.left), height(node.right)) + 1;
return node;
}
/**
* 先左旋再右旋
* 1.先將該節點的左孩子節點左旋;
* 2.再將旋轉後的節點,右旋;
* 3.重新計算旋轉後各節點的高度;
* 4.旋轉效果:
* 3 | 3 | 2
* / | / | / \
* 1 -> 2 -> 1 3
* \ | / |
* 2 | 1 |
* @param node
* @return
*/
private Node leftAndRightRotate(Node node) {
node.left = this.leftRotate(node.left);
return rightRotate(node);
}
/**
* 先右旋再左旋
* 1.先將該節點的右孩子節點右旋;
* 2.先將上一步旋轉完成的節點,左旋;
* 3.再重新計算旋轉後各節點的高度;
* 4.旋轉效果:
* 1 | 1 | 2
* \ | \ | / \
* 3 -> 2 -> 1 3
* / | \ |
* 2 | 3 |
* @param node
* @return
*/
private Node rightAndLeftRotate(Node node) {
node.right = this.rightRotate(node.right);
return leftRotate(node);
}
/**
* 左旋
* 1.以傳入的節點的右節點爲支點,將傳入的節點旋轉到該支點節點的左節點上;
* 2.重新計算旋轉後的節點的高度;
* 3.旋轉效果:
* 1 | 2
* \ | / \
* 2 -> 1 3
* \ |
* 3 |
* @param oldNode
* @return
*/
private Node leftRotate(Node oldNode) {
Node newNode = oldNode.right;
oldNode.right = newNode.left;
newNode.left = oldNode;
oldNode.nodeHeight = Math.max(height(oldNode.left), height(oldNode.right)) + 1;
newNode.nodeHeight = Math.max(height(newNode.right), oldNode.nodeHeight) + 1;
return newNode;
}
/**
* 右旋
* 1.以傳入的節點的左節點爲支點,將傳入的節點旋轉到該支點節點的右節點上;
* 2.重新計算旋轉後的節點的高度;
* 3.旋轉效果:
* 3 | 2
* / | / \
* 2 -> 1 3
* / |
* 1 |
* @param oldNode
* @return
*/
private Node rightRotate(Node oldNode) {
Node newNode = oldNode.left;
oldNode.left = newNode.right;
newNode.right = oldNode;
oldNode.nodeHeight = Math.max(height(oldNode.left), height(oldNode.right)) + 1;
newNode.nodeHeight = Math.max(height(newNode.left), oldNode.nodeHeight) + 1;
return newNode;
}
/**
* 獲取節點高度
* 1.節點爲空,則高度爲-1;
* 2.否則返回節點高度;
*
* @param node
* @return
*/
private int height(Node node) {
return node == null ? -1 : node.nodeHeight;
}
}