人生註定負重登山,攀高峯,陷低谷,處逆境,一波三折是人生之必然,一切的坎坷只是暫時的,
找到解決問題的切入點,一笑而過,坎坷會使我們更成熟,更完美,更堅強地撐起自己的一片心靈天空。
什麼是平衡二叉樹(AVL樹)
AVL樹是二分搜索樹的優化版,又稱平衡二叉樹。
二分搜索樹的性能分析:
根據二分搜索樹的特點,如果給定一個有序的序列來創建二分搜索樹,比如給定序列爲:1,2,3,4,5,6 那麼就會得到如右側這樣的向一側偏斜的結構,事實上向一側偏斜的二分搜索樹其實就是一個鏈表。二分搜索樹在最好的情況下只需要O(logn)的時間複雜度,但是在最壞的情況下,二分搜索樹退化成鏈表,相應的就變成了O(n)的時間複雜度。這種情況主要是由於二叉樹中結點分佈不均衡導致的。
平衡二叉樹解決了由於插入或刪除所導致的結點分佈不均的問題。
平衡二叉樹或者是一棵空樹; 或者是具有如下特性的二叉排序樹:
- 二叉排序樹中任何一個結點的左子樹和右子樹高度相差的絕對值最多爲1;
- 它的左、右子樹也分別都是平衡二叉樹。
舉個栗子:
跟據定義,平衡二叉樹首先應該滿足二分搜索樹的性質,簡單驗證一下,這棵樹的確是個二分搜索樹,其次,這棵樹還應滿足任一結點的左子樹和右子樹高度相差最多爲1,我們把每個節點的高度都標記出來,其中節點爲空的高度應該爲0,我們發現,雖然這棵樹看起來不是那麼的平衡,但它的確是一顆平衡二叉樹。
爲了方便,我們將引入一個平衡因子(Balance Factor), 並且平衡因子 = 左子樹的高度 - 右子樹的高度。
顯然,如果平衡因子的絕對值大於1,那這顆樹就不是平衡二叉樹。
平衡二叉樹的添加元素
其實平衡二叉樹也是一個二分搜索樹,所以平衡二叉樹的插入元素就是二分搜索樹的插入元素邏輯,其實也是一個查找插入的過程,如果想要具體瞭解二分搜索樹的插入元素,請看我關於二叉樹的那篇博客:
數據結構:樹形結構之樹及二叉樹
雖然插入的邏輯是一樣的,但是向一顆平衡二叉樹中插入一個元素後,並不能保證這棵樹依然保持平衡二叉樹的性質,當插入元素後,整棵樹將出現4種失衡的情況。
LL型
如下圖所示:導致不平衡的節點爲y的左子樹(L)的左子樹(L),y的平衡因子爲2。
一般情況下:x,y,z可能存在子樹,但y的平衡因子大於1
那麼如何調整才能使得這棵樹滿足平衡二叉樹的性質呢?這個操作就叫右旋轉。
旋轉過程如下:
首先將x的右子樹指向y,原來x的右子樹則連接到y的左子樹上。兩步操作後,x,y,z,T1,T2,T3,T4的大小關係依然保持不變。
RR型
RR型是與LL型相對稱的操作,即導致不平衡的節點爲y的右子樹®的右子樹®。
具體的操作就是左旋轉,下面就是左旋轉的具體過程:
LR型
LR型:導致不平衡的節點爲y的左子樹(L)的右子樹®。即:
由於x,y,z,T1,T2,T3,T4之間的大小關係,因此LR型不能簡單的用左旋轉或右旋轉來實現平衡的調整。
但是我們如果對x進行一次左旋轉就能得到如下結構:
對x進行左旋轉,即:z.left = x; x.right = T2
之後便將LR型轉換成了LL型,再按照LL型的以y爲根節點進行右旋轉就可以了。
RL型
RL型是與LR型對稱的操作,具體操作如下圖:
再對RR型以y爲根節點進行一次左旋轉就OK了。
平衡二叉樹的刪除元素
平衡二叉樹的刪除元素其實也是二分搜索樹的刪除元素邏輯,我關於二叉樹的那篇博客有詳細介紹:數據結構:樹形結構之樹及二叉樹
只不過需要在刪除結點之後維護二叉樹的平衡型,同樣也是分爲上面四種失衡情況,這裏不再贅述。
平衡二叉樹的實現
package cn.boom.tree;
import java.util.ArrayList;
public class AVLTree<T extends Comparable<T>> {
private Node root;
private int size;
private class Node {
private T data;
private int height;
private Node left;
private Node right;
public Node() {
this.data = null;
this.left = null;
this.right = null;
this.height = 1;
}
public Node(T data) {
this.data = data;
this.left = null;
this.right = null;
this.height = 1;
}
}
public AVLTree() {
root = null;
size = 0;
}
/**
* 獲取樹中元素個數
*
* @return
*/
public int getSize() {
return size;
}
/**
* 樹是否爲空
*
* @return
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 向二分搜索樹中插入元素
*
* @param elem
*/
public void add(T elem) {
root = add(root, elem);
size++;
}
private Node add(Node parent, T e) {
if (parent == null) {
return new Node(e);
}
if (e.compareTo(parent.data) > 0) { //忽略重複元素
parent.right = add(parent.right, e);
} else if (e.compareTo(parent.data) < 0) {
parent.left = add(parent.left, e);
}
//維護結點高度
parent.height = Math.max(getHeight(parent.left), getHeight(parent.right)) + 1;
//維護平衡性
int balanceFactor = getBalanceFactor(parent);
//LL型
if (balanceFactor > 1 && getBalanceFactor(parent.left) >= 0) {
return rightRotate(parent);
}
//RR型
if (balanceFactor < -1 && getBalanceFactor(parent.right) <= 0) {
return leftRotate(parent);
}
//LR型
if (balanceFactor > 1 && getBalanceFactor(parent.left) < 0) {
parent.left = leftRotate(parent.left);
return rightRotate(parent);
}
//RL型
if (balanceFactor < -1 && getBalanceFactor(parent.left) > 0) {
parent.right = rightRotate(parent.right);
return leftRotate(parent);
}
return parent;
}
/**
* 元素e是否存在
*
* @param e
* @return
*/
public boolean contains(T e) {
return contains(root, e);
}
private boolean contains(Node parent, T e) {
if (parent != null) {
if (e.compareTo(parent.data) == 0) {
return true;
} else if (e.compareTo(parent.data) > 0) {
return contains(parent.right, e);
} else if (e.compareTo(parent.data) < 0) {
return contains(parent.left, e);
}
}
return false;
}
// 獲得節點node的高度
private int getHeight(Node node){
if(node == null)
return 0;
return node.height;
}
//獲得該節點的平衡因子值
private int getBalanceFactor(Node node) {
if (node == null) {
return 0;
}
return getHeight(node.left) - getHeight(node.right);
}
// 對節點y進行向右旋轉操作,返回旋轉後新的根節點x
// y x
// / \ / \
// x T4 向右旋轉 (y) z y
// / \ - - - - - - - -> / \ / \
// z T3 T1 T2 T3 T4
// / \
// T1 T2
private Node rightRotate(Node y){
Node x = y.left;
Node T3 = x.right;
x.right = y;
y.left = T3;
//維護x和y的高度
y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
return x;
}
// 對節點y進行向左旋轉操作,返回旋轉後新的根節點x
// y x
// / \ / \
// T1 x 向左旋轉 (y) y z
// / \ - - - - - - - -> / \ / \
// T2 z T1 T2 T3 T4
// / \
// T3 T4
private Node leftRotate(Node y) {
Node x = y.right;
Node T2 = x.left;
x.left = y;
y.right = T2;
//維護x和y的高度
y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
return x;
}
/**
* 找最小值
*
* @return
*/
public T minMum() {
return minMum(root).data;
}
private Node minMum(Node parent) {
if (parent == null) {
throw new IllegalArgumentException(" BST is empty !");
}
if (parent.left == null) {
return parent;
} else {
return minMum(parent.left);
}
}
/**
* 找最大值
*
* @return
*/
public T maxMum() {
return maxMum(root).data;
}
private Node maxMum(Node parent) {
if (parent == null) {
throw new IllegalArgumentException(" BST is empty !");
}
if (parent.right == null) {
return parent;
} else {
return maxMum(parent.right);
}
}
/**
* 刪除最小元素
*
* @return
*/
public T removeMin() {
T minElem = minMum();
root = removeMin(root);
return minElem;
}
/**
* 返回刪除結點後的根節點
*
* @param parent
* @return
*/
private Node removeMin(Node parent) {
if (parent == null) {
throw new IllegalArgumentException(" BST is empty !");
}
if (parent.left == null) {
Node rightNode = parent.right;
parent.right = null;
size--;
return rightNode;
} else {
parent.left = removeMin(parent.left);
return parent;
}
}
/**
* 刪除最大元素
*
* @return
*/
public T removeMax() {
T elem = maxMum();
root = removeMax(root);
return elem;
}
/**
* 遞歸刪除最大元素,返回根節點
*
* @param parent
* @return
*/
private Node removeMax(Node parent) {
if (parent == null) {
throw new IllegalArgumentException(" AVLTree is empty !");
}
if (parent.right == null) {
Node leftNode = parent.left;
parent.left = null;
size--;
return leftNode;
} else {
parent.right = removeMax(parent.right);
return parent;
}
}
/**
* 刪除元素e
*
* @param e
*/
public void remove(T e) {
root = remove(root, e);
}
public Node remove(Node parent, T e) {
if (parent == null) {
return null;
}
Node retNode;
if (e.compareTo(parent.data) > 0) {
parent.right = remove(parent.right, e);
retNode = parent;
} else if (e.compareTo(parent.data) < 0) {
parent.left = remove(parent.left, e);
retNode = parent;
} else {
if (parent.left == null) {
Node rightNode = parent.right;
parent.right = null;
size--;
retNode = rightNode;
}else if (parent.right == null) {
Node leftNode = parent.left;
parent.left = null;
size--;
retNode = leftNode;
}else {
//左右子樹均不爲空
//從右子樹中得到比待刪結點大的最小元素結點,並用此節點頂替刪除結點
Node replaceNode = minMum(parent.right);//removeMin中size--,因此不需要在維護size了
replaceNode.right = remove(parent.right,replaceNode.data);
replaceNode.left = parent.left;
parent.left = parent.right = null;
retNode = replaceNode;
}
}
if(retNode == null)
return null;
// 更新height
retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));
// 計算平衡因子
int balanceFactor = getBalanceFactor(retNode);
// 平衡維護
// LL
if (balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)
return rightRotate(retNode);
// RR
if (balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0)
return leftRotate(retNode);
// LR
if (balanceFactor > 1 && getBalanceFactor(retNode.left) < 0) {
retNode.left = leftRotate(retNode.left);
return rightRotate(retNode);
}
// RL
if (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0) {
retNode.right = rightRotate(retNode.right);
return leftRotate(retNode);
}
return retNode;
}
// 判斷該二叉樹是否是一棵平衡二叉樹
public boolean isBalanced(){
return isBalanced(root);
}
// 判斷以Node爲根的二叉樹是否是一棵平衡二叉樹,遞歸算法
private boolean isBalanced(Node node){
if(node == null)
return true;
int balanceFactor = getBalanceFactor(node);
if(Math.abs(balanceFactor) > 1)
return false;
return isBalanced(node.left) && isBalanced(node.right);
}
}