引言
前兩篇文章中,我們學(xué)習(xí)了二叉樹的結(jié)構(gòu)、性質(zhì)、實現(xiàn)以及應(yīng)用,它的操作時間復(fù)雜度為O(lgn),但注意這個復(fù)雜度從嚴(yán)格意義上來說不太準(zhǔn)確,在某一棵子樹子樹的深度遠(yuǎn)遠(yuǎn)大于另外一顆子樹時,查找效率會上升到O(n),如下面的情況:
從上圖可以看出,按照排序數(shù)組構(gòu)建二叉樹時,它變成了單向的二叉樹,本質(zhì)和鏈表沒什么區(qū)別,操作復(fù)雜度為O(n)。這個問題該如何解決呢?我們引入"子樹平衡"這一性質(zhì),來保證左右的葉子節(jié)點不會"埋"的太深,這種樹被稱為平衡二叉樹。
平衡二叉樹具有如下性質(zhì):
1.具備二叉排序樹的所有性質(zhì);
2.左右子樹的高度相差絕對值不大于1;
3.左右子樹均為平衡二叉樹.
本文我們規(guī)定葉子節(jié)點的高度為0,空節(jié)點高度為-1,父節(jié)點高度為左右子樹中較大高度+1。
二叉樹的失衡情況分析
當(dāng)左右子樹的高度相差大于等于2時,表示該根節(jié)點失衡,共有四種失衡情況:
① 左子樹的左子樹高度-左子樹的右子樹高度>=2,簡稱為LL型;
②左子樹的右子樹高度-左子樹的左子樹高度>=2,簡稱LR型;
③右子樹的左子樹高度-右子樹的右子樹高度>=2,簡稱為RL型;
④ 右子樹的右子樹高度-右子樹的左子樹高度>=2,簡稱RR型;
其中① 和④ 為一對,需要做但旋轉(zhuǎn);②和③為一對,需要做雙旋轉(zhuǎn)。下面詳細(xì)分析。
先看看節(jié)點結(jié)構(gòu),和搜索樹相比,多了高度字段,用于平衡性判斷。
package tree.avltree;
/**
* Created by chenming on 2018/6/5
* 平衡二叉樹節(jié)點,多了高度字段
*/
public class AVLNode<T extends Comparable> {
public AVLNode<T> left;//左結(jié)點
public AVLNode<T> right;//右結(jié)點
public T data;
public int height;//當(dāng)前結(jié)點的高度
public AVLNode(T data) {
this(null, null, data);
}
public AVLNode(AVLNode<T> left, AVLNode<T> right, T data) {
this(left, right, data, 0);
}
public AVLNode(AVLNode<T> left, AVLNode<T> right, T data, int height) {
this.left = left;
this.right = right;
this.data = data;
this.height = height;
}
/**
* 判斷是否為葉子結(jié)點
*
* @return
*/
public boolean isLeaf() {
return this.left == null && this.right == null;
}
}
LL型單旋
LL型單旋如下圖圖1:
當(dāng)x的左子樹w的左子樹高度為2,而x的右子樹高度為1,導(dǎo)致x節(jié)點失衡,需要以x為中心做右旋操作,結(jié)構(gòu)變成圖2,w變成根節(jié)點,x變成w的右子樹,w的右子樹掛載到x的左邊,二叉樹重新平衡。旋轉(zhuǎn)操作后,只需要更新w與x的高度即可。LL右旋代碼如下:
/**
* 向左子樹的左節(jié)點插入元素,執(zhí)行右旋操作
*
* @param x
* @return
*/
private AVLNode<T> singleRotateLeft(AVLNode<T> x) {
AVLNode<T> w = x.left;
x.left = w.right;
w.right = x;
//重新計算x w的高度
x.height = Math.max(height(x.left), height(x.right)) + 1;
w.height = Math.max(height(w.left), x.height) + 1;
return w;
}
RR型單旋
RR型號單旋如下圖1:
RR型與LL型正好對稱,x的右子樹導(dǎo)致失衡,需要將x左旋,旋轉(zhuǎn)后結(jié)構(gòu)如圖2.x為根節(jié)點,w為x的左子樹,x的左子樹掛載到w的右子樹上。此時,樹重新獲得平衡。然后更新w和x的高度即可。代碼如下:
/**
* 向右子樹的右節(jié)點插入元素,執(zhí)行左旋操作
*
* @param w
* @return
*/
private AVLNode<T> singleRotateRight(AVLNode<T> w) {
AVLNode<T> x = w.right;
w.right = x.left;
x.left = w;
//重新計算x/w的高度
w.height = Math.max(height(w.left), height(w.right)) + 1;
x.height = Math.max(height(x.left), w.height) + 1;
//返回新的根結(jié)點
return x;
}
RL和LR雙旋轉(zhuǎn)
對于RL和LR,僅僅做單旋轉(zhuǎn)無法恢復(fù)平衡,看下圖:
Y的高度為2,僅做LL單旋的話,如圖2,無法恢復(fù)平衡,此時需要雙旋轉(zhuǎn)實現(xiàn)平衡,先處理好w子樹,將Y做左旋轉(zhuǎn),然后再對Y做右旋轉(zhuǎn).操作流程如下圖:
先將y以w為中心做右旋,然后將y以x為中心做右旋。雙旋后的結(jié)構(gòu)如圖3,根節(jié)點為y,它的子樹被拆分到兩個子樹w和x上,實現(xiàn)樹的"扁平化"。由于是基于單旋操作,代碼實現(xiàn)比較簡單:
/**
* 往左子樹的右孩子插入節(jié)點,左右雙旋
*/
private AVLNode<T> doubleRotateWithLeft(AVLNode<T> x) {
//w先進行右右單旋轉(zhuǎn)
x.left = singleRotateRight(x.left);
//x進行左左但旋
return singleRotateLeft(x);
}
相對應(yīng)的LR雙旋轉(zhuǎn)操作流程如下圖:
旋轉(zhuǎn)原理和LR一樣,這里不再廢話了,大家看圖就能明白。代碼:
/**
* 右左旋轉(zhuǎn)(RL旋轉(zhuǎn))
*
* @param x
* @return
*/
private AVLNode<T> doubleRotateWithRight(AVLNode<T> x) {
//先進行LL旋轉(zhuǎn)
x.right = singleRotateLeft(x.right);
//再進行RR旋轉(zhuǎn)
return singleRotateRight(x);
}
好了,本篇最核心的內(nèi)容分析完畢,下面分析平衡二叉樹的構(gòu)建和刪除操作,遵循下面兩條原則:
1>判斷平衡性,如果不平衡需要走2>
2>分析操作節(jié)點屬于哪種旋轉(zhuǎn)類型;
3>操作完成后,別忘記更新新節(jié)點高度.
平衡二叉樹的構(gòu)建
/**
* 平衡二叉樹的插入操作
*
* @param data
* @param p
* @return
*/
private AVLNode<T> insert(T data, AVLNode<T> p) {
if (p == null) {
p = new AVLNode<>(data);
} else if (data.compareTo(p.data) < 0) {//向左子樹尋找插入位置{
p.left = insert(data, p.left);
if (height(p.left) - height(p.right) == 2) {//子樹高度差為2,需要平衡
if (data.compareTo(p.left.data) < 0) {//往左邊插入LL單旋轉(zhuǎn)
p = singleRotateLeft(p);
} else {
p = doubleRotateWithLeft(p);//往左邊插入LR雙旋轉(zhuǎn)
}
}
} else if (data.compareTo(p.data) > 0) {//向右子樹尋找插入位置
p.right = insert(data, p.right);
if (height(p.right) - height(p.left) == 2) {
if (data.compareTo(p.right.data) < 0) {
//進行RL雙旋轉(zhuǎn)
p = doubleRotateWithRight(p);
} else {
//進行RR單旋
p = singleRotateRight(p);
}
}
}
//重新計算節(jié)點高度
if (p != null) {
p.height = Math.max(height(p.left), height(p.right)) + 1;
}
return p;
}
平衡二叉樹的插入操作是在搜索二叉樹的基礎(chǔ)上,判斷插入后的新節(jié)點的平衡性,如果不平衡,則判斷是四種情況的哪一種,做相應(yīng)操作,最后更新旋轉(zhuǎn)后的新根節(jié)點的高度。
平衡二叉樹的刪除操作
平衡二叉樹的刪除相對搜索二叉樹的刪除更為復(fù)雜(搜索二叉樹的刪除操作也不簡單,哭了。。),這里平衡二叉樹的刪除有一個特征:待刪除節(jié)點左子樹高度大于右子樹高度,則找在左子樹中尋找前驅(qū)節(jié)點替換當(dāng)前節(jié)點后,它仍為平衡二叉樹,反之則用右子樹的后繼節(jié)點替換它。和插入操作類似,做遞歸刪除后需要判斷刪除后新節(jié)點的平衡性,如果不平衡則判斷是四種情況的哪一種,做相應(yīng)操作,最后更新新子樹的高度即可。刪除代碼不做詳細(xì)解釋了,比較復(fù)雜,代碼注釋應(yīng)該比較清晰了。下面的代碼在搜索二叉樹的基礎(chǔ)上,借鑒了這篇博客中刪除節(jié)點的思路https://www.cnblogs.com/Camilo/p/3917041.html,目前親測有效,如有誤人子弟,歡迎打臉,還望見諒!
刪除節(jié)點代碼:
/**
* 非平衡二叉樹的刪除步驟:
* ① 如果要刪除的結(jié)點q恰好是葉子結(jié)點,那么它可以立即被刪除
* ②如果要刪除的結(jié)點q擁有一個孩子結(jié)點,則應(yīng)該調(diào)整要被刪除的
* 父結(jié)點(p.left 或 p.right)指向被刪除結(jié)點的孩子結(jié)點(q.left 或 q.right)
* ③如果要刪除的結(jié)點q擁有兩個孩子結(jié)點,則刪除策略是用q的右子樹的最小的數(shù)據(jù)替代要被刪除結(jié)點的數(shù)據(jù),
* 并遞歸刪除用于替換的結(jié)點(此時該結(jié)點已為空),此時二叉查找樹的結(jié)構(gòu)并不會被打亂,其特性仍舊生效。
* 采用這樣策略的主要原因是右子樹的最小結(jié)點的數(shù)據(jù)替換要被刪除的結(jié)點后可以滿足維持二叉查找樹的結(jié)構(gòu)和特性,
* 又因為右子樹最小結(jié)點不可能有左孩子,刪除起來也相對簡單些。
* 平衡二叉樹的刪除方法在之前的基礎(chǔ)上需要考慮下面兩點
* ① 當(dāng)前待刪除節(jié)點左子樹高度大于右子樹高度,則找在左子樹中尋找前驅(qū)節(jié)點替換當(dāng)前節(jié)點
* ② 刪除操作執(zhí)行后,別忘了重新更新根節(jié)點高度,然后根據(jù)左右子樹的高度判斷平衡性,根據(jù)前面分析的四種情況進行旋轉(zhuǎn)
*
* @param data
* @param rootNode 當(dāng)前操作節(jié)點
* @return
*/
public AVLNode<T> removeAvlNode(T data, AVLNode<T> rootNode) {
if (data == null) {
return rootNode;
}
if (rootNode == null) {
return rootNode;//沒有找到
}
int compareResult = data.compareTo(rootNode.data);
if (compareResult < 0) {//左子樹遞歸刪除
rootNode.left = removeAvlNode(data, rootNode.left);
// 高度計算,判斷是否失衡
//刪除后,修改樹的高度,左子樹刪除,
rootNode.height = Math.max(height(rootNode.left), height(rootNode.right)) + 1;
//左子樹刪除后,判斷是否失衡
if (height(rootNode.right) - height(rootNode.left) == 2) {
//調(diào)整右子樹
if (height(rootNode.right.left) > height(rootNode.right.right)) {
//右子樹的左子樹導(dǎo)致失衡,則進行右左雙旋轉(zhuǎn)
rootNode = doubleRotateWithRight(rootNode);
} else {
//右子樹的右子樹導(dǎo)致失衡,則進行右右單旋轉(zhuǎn)
rootNode = singleRotateRight(rootNode);
}
}
} else if (compareResult > 0) {//右子樹遞歸刪除
rootNode.right = removeAvlNode(data, rootNode.right);
//高度計算,判斷是否失衡
//刪除后,修改樹的高度
rootNode.height = Math.max(height(rootNode.left), height(rootNode.right)) + 1;
//右子樹刪除后,判斷rootNode是否失衡
if (height(rootNode.left) - height(rootNode.right) == 2) {
//調(diào)整右子樹
if (height(rootNode.left.left) > height(rootNode.left.right)) {
//左子樹的左子樹導(dǎo)致失衡,則進行左左單旋轉(zhuǎn)
rootNode = singleRotateLeft(rootNode);
} else {
//左子樹的右子樹導(dǎo)致失衡,則進行左右單旋轉(zhuǎn)
rootNode = doubleRotateWithLeft(rootNode);
}
}
} else if (rootNode.left != null && rootNode.right != null) {//倆孩子節(jié)點
//當(dāng)待刪除結(jié)點左子樹的高度大于右子樹的高度時,用*T的前驅(qū)結(jié)點pre代替*T,
//再將結(jié)點pre從樹中刪除。這樣可以保證刪除結(jié)點后的樹仍為二叉平衡樹。
if (height(rootNode.left) > height(rootNode.right)) {//找前驅(qū)節(jié)點替換
rootNode.data = findMax(rootNode.left).data;
rootNode.left = removeAvlNode(rootNode.data, rootNode.left);
} else {//后繼節(jié)點替換
//右子樹的最小值替換當(dāng)前節(jié)點值
rootNode.data = findMin(rootNode.right).data;
//移除用于替換的點
rootNode.right = removeAvlNode(rootNode.data, rootNode.right);
}
} else {//只有一個孩子節(jié)點或者葉子節(jié)點的情況,次處返回的節(jié)點返回給上次遞歸,用于父節(jié)點鏈接該節(jié)點
rootNode = (rootNode.left != null) ? rootNode.left : rootNode.right;
}
return rootNode;
}
/**
* 查找最小值結(jié)點
*
* @param p
* @return
*/
private AVLNode<T> findMin(AVLNode<T> p) {
if (p == null)//結(jié)束條件
return null;
else if (p.left == null)//如果沒有左結(jié)點,那么t就是最小的
return p;
return findMin(p.left);
}
/**
* 查找最大值結(jié)點
*
* @param p
* @return
*/
private AVLNode<T> findMax(AVLNode<T> p) {
if (p == null)
return null;
else if (p.right == null)//如果沒有右結(jié)點,那么t就是最大的
return p;
return findMax(p.right);
}
/**
* @param p
* @return
*/
public int height(AVLNode<T> p) {
return p == null ? -1 : p.height;
}
完整代碼
除了插入元素和刪除,其他方法與搜索二叉樹基本一樣,不再說明。
package tree.avltree;
import queue.Queue;
import tree.BinaryNode;
import tree.Tree;
/**
* Created by chenming on 2018/6/5
*/
public class AVLTree<T extends Comparable> implements Tree<T> {
public AVLNode<T> mRoot;//為了測試用,mRoot先暴露出去
/**
* 向左子樹的左節(jié)點插入元素,執(zhí)行右旋操作
*
* @param x
* @return
*/
private AVLNode<T> singleRotateLeft(AVLNode<T> x) {
AVLNode<T> w = x.left;
x.left = w.right;
w.right = x;
//重新計算x w的高度
x.height = Math.max(height(x.left), height(x.right)) + 1;
w.height = Math.max(height(w.left), x.height) + 1;
return w;
}
/**
* 向右子樹的右節(jié)點插入元素,執(zhí)行左旋操作
*
* @param w
* @return
*/
private AVLNode<T> singleRotateRight(AVLNode<T> w) {
AVLNode<T> x = w.right;
w.right = x.left;
x.left = w;
//重新計算x/w的高度
w.height = Math.max(height(w.left), height(w.right)) + 1;
x.height = Math.max(height(x.left), w.height) + 1;
//返回新的根結(jié)點
return x;
}
/**
* 往左子樹的右孩子插入節(jié)點,左右雙旋
*/
private AVLNode<T> doubleRotateWithLeft(AVLNode<T> x) {
//w先進行右右單旋轉(zhuǎn)
x.left = singleRotateRight(x.left);
return singleRotateLeft(x);
}
/**
* 右左旋轉(zhuǎn)(RL旋轉(zhuǎn))
*
* @param x
* @return
*/
private AVLNode<T> doubleRotateWithRight(AVLNode<T> x) {
//先進行LL旋轉(zhuǎn)
x.right = singleRotateLeft(x.right);
//再進行RR旋轉(zhuǎn)
return singleRotateRight(x);
}
@Override
public void insert(T data) {
mRoot = insert(data, mRoot);
}
/**
* 平衡二叉樹的插入操作
*
* @param data
* @param p
* @return
*/
private AVLNode<T> insert(T data, AVLNode<T> p) {
if (p == null) {
p = new AVLNode<>(data);
} else if (data.compareTo(p.data) < 0) {//向左子樹尋找插入位置{
p.left = insert(data, p.left);
if (height(p.left) - height(p.right) == 2) {//子樹高度差為2,需要平衡
if (data.compareTo(p.left.data) < 0) {//往左邊插入LL單旋轉(zhuǎn)
p = singleRotateLeft(p);
} else {
p = doubleRotateWithLeft(p);//往左邊插入LR雙旋轉(zhuǎn)
}
}
} else if (data.compareTo(p.data) > 0) {//向右子樹尋找插入位置
p.right = insert(data, p.right);
if (height(p.right) - height(p.left) == 2) {
if (data.compareTo(p.right.data) < 0) {
//進行RL雙旋轉(zhuǎn)
p = doubleRotateWithRight(p);
} else {
//進行RR單旋
p = singleRotateRight(p);
}
}
}
//重新計算節(jié)點高度
if (p != null) {
p.height = Math.max(height(p.left), height(p.right)) + 1;
}
return p;
}
/**
* 刪除操作
*
* @param data
*/
@Override
public void remove(T data) {
// mRoot = remove(mRoot, data);
mRoot = removeAvlNode(data, mRoot);
}
/**
* 非平衡二叉樹的刪除步驟:
* ① 如果要刪除的結(jié)點q恰好是葉子結(jié)點,那么它可以立即被刪除
* ②如果要刪除的結(jié)點q擁有一個孩子結(jié)點,則應(yīng)該調(diào)整要被刪除的
* 父結(jié)點(p.left 或 p.right)指向被刪除結(jié)點的孩子結(jié)點(q.left 或 q.right)
* ③如果要刪除的結(jié)點q擁有兩個孩子結(jié)點,則刪除策略是用q的右子樹的最小的數(shù)據(jù)替代要被刪除結(jié)點的數(shù)據(jù),
* 并遞歸刪除用于替換的結(jié)點(此時該結(jié)點已為空),此時二叉查找樹的結(jié)構(gòu)并不會被打亂,其特性仍舊生效。
* 采用這樣策略的主要原因是右子樹的最小結(jié)點的數(shù)據(jù)替換要被刪除的結(jié)點后可以滿足維持二叉查找樹的結(jié)構(gòu)和特性,
* 又因為右子樹最小結(jié)點不可能有左孩子,刪除起來也相對簡單些。
* 平衡二叉樹的刪除方法在之前的基礎(chǔ)上需要考慮下面兩點
* ① 當(dāng)前待刪除節(jié)點左子樹高度大于右子樹高度,則找在左子樹中尋找前驅(qū)節(jié)點替換當(dāng)前節(jié)點
* ② 刪除操作執(zhí)行后,別忘了重新更新根節(jié)點高度,然后根據(jù)左右子樹的高度判斷平衡性,根據(jù)前面分析的四種情況進行旋轉(zhuǎn)
*
* @param data
* @param rootNode 當(dāng)前操作節(jié)點
* @return
*/
public AVLNode<T> removeAvlNode(T data, AVLNode<T> rootNode) {
if (data == null) {
return rootNode;
}
if (rootNode == null) {
return rootNode;//沒有找到
}
int compareResult = data.compareTo(rootNode.data);
if (compareResult < 0) {//左子樹遞歸刪除
rootNode.left = removeAvlNode(data, rootNode.left);
// 高度計算,判斷是否失衡
//刪除后,修改樹的高度,左子樹刪除,
rootNode.height = Math.max(height(rootNode.left), height(rootNode.right)) + 1;
//左子樹刪除后,判斷是否失衡
if (height(rootNode.right) - height(rootNode.left) == 2) {
//調(diào)整右子樹
if (height(rootNode.right.left) > height(rootNode.right.right)) {
//右子樹的左子樹導(dǎo)致失衡,則進行右左雙旋轉(zhuǎn)
rootNode = doubleRotateWithRight(rootNode);
} else {
//右子樹的右子樹導(dǎo)致失衡,則進行右右單旋轉(zhuǎn)
rootNode = singleRotateRight(rootNode);
}
}
} else if (compareResult > 0) {//右子樹遞歸刪除
rootNode.right = removeAvlNode(data, rootNode.right);
//高度計算,判斷是否失衡
//刪除后,修改樹的高度
rootNode.height = Math.max(height(rootNode.left), height(rootNode.right)) + 1;
//右子樹刪除后,判斷rootNode是否失衡
if (height(rootNode.left) - height(rootNode.right) == 2) {
//調(diào)整右子樹
if (height(rootNode.left.left) > height(rootNode.left.right)) {
//左子樹的左子樹導(dǎo)致失衡,則進行左左單旋轉(zhuǎn)
rootNode = singleRotateLeft(rootNode);
} else {
//左子樹的右子樹導(dǎo)致失衡,則進行左右單旋轉(zhuǎn)
rootNode = doubleRotateWithLeft(rootNode);
}
}
} else if (rootNode.left != null && rootNode.right != null) {//倆孩子節(jié)點
//當(dāng)待刪除結(jié)點左子樹的高度大于右子樹的高度時,用*T的前驅(qū)結(jié)點pre代替*T,
//再將結(jié)點pre從樹中刪除。這樣可以保證刪除結(jié)點后的樹仍為二叉平衡樹。
if (height(rootNode.left) > height(rootNode.right)) {//找前驅(qū)節(jié)點替換
rootNode.data = findMax(rootNode.left).data;
rootNode.left = removeAvlNode(rootNode.data, rootNode.left);
} else {//后繼節(jié)點替換
//右子樹的最小值替換當(dāng)前節(jié)點值
rootNode.data = findMin(rootNode.right).data;
//移除用于替換的點
rootNode.right = removeAvlNode(rootNode.data, rootNode.right);
}
} else {//只有一個孩子節(jié)點或者葉子節(jié)點的情況,次處返回的節(jié)點返回給上次遞歸,用于父節(jié)點鏈接該節(jié)點
rootNode = (rootNode.left != null) ? rootNode.left : rootNode.right;
}
return rootNode;
}
/**
* 查找最小值結(jié)點
*
* @param p
* @return
*/
private AVLNode<T> findMin(AVLNode<T> p) {
if (p == null)//結(jié)束條件
return null;
else if (p.left == null)//如果沒有左結(jié)點,那么t就是最小的
return p;
return findMin(p.left);
}
/**
* 查找最大值結(jié)點
*
* @param p
* @return
*/
private AVLNode<T> findMax(AVLNode<T> p) {
if (p == null)
return null;
else if (p.right == null)//如果沒有右結(jié)點,那么t就是最大的
return p;
return findMax(p.right);
}
/**
* @param p
* @return
*/
public int height(AVLNode<T> p) {
return p == null ? -1 : p.height;
}
@Override
public boolean isEmpty() {
return mRoot == null;
}
@Override
public int size() {
return size(mRoot);
}
/**
* 遞歸計算左右子樹的和,再加上當(dāng)前節(jié)點
*
* @param currentNode
* @return
*/
public int size(AVLNode<T> currentNode) {
if (currentNode == null) {
return 0;
}
int leftSize = size(currentNode.left);
int rightSize = size(currentNode.right);
return leftSize + rightSize + 1;
}
@Override
public int height() {
return mRoot == null ? 0 : mRoot.height;
}
@Override
public String preOrder() {
return preOrder(mRoot);
}
/**
* 前序遍歷,先遍歷節(jié)點,再遍歷左子樹,最后遍歷右子樹
*
* @return
*/
public String preOrder(AVLNode<T> root) {
StringBuilder sb = new StringBuilder();
if (root != null) {
sb.append(root.data + ", ");
sb.append(preOrder(root.left));
sb.append(preOrder(root.right));
}
return sb.toString();
}
@Override
public String inOrder() {
return inOrder(mRoot);
}
/**
* 遞歸中序遍歷
*
* @param root
* @return
*/
public String inOrder(AVLNode<T> root) {
StringBuilder sb = new StringBuilder();
if (root != null) {
sb.append(inOrder(root.left));
sb.append(root.data + ", ");
sb.append(inOrder(root.right));
}
return sb.toString();
}
@Override
public String postOrder() {
return postOrder(mRoot);
}
/**
* 遞歸中序遍歷
*
* @param root
* @return
*/
public String postOrder(AVLNode<T> root) {
StringBuilder sb = new StringBuilder();
if (root != null) {
sb.append(postOrder(root.left));
sb.append(postOrder(root.right));
sb.append(root.data + ", ");
}
return sb.toString();
}
/**
* 層遍歷:關(guān)鍵在于隊列保存節(jié)點的兩個孩子節(jié)點,每次迭代做如下操作:
* 1.將非空子節(jié)點入隊
* 2.輸出隊列頭節(jié)點
*
* @return
*/
@Override
public String levelOrder() {
return levelOrder(mRoot);
}
/**
* 層遍歷
*
* @param node
* @return
*/
public String levelOrder(AVLNode<T> node) {
StringBuilder sb = new StringBuilder();
Queue<AVLNode<T>> queue = new Queue<>();
while (node != null) {
sb.append(node.data + ", ");
AVLNode<T> leftNode = node.left;
AVLNode<T> rightNode = node.right;
//同層的子節(jié)點入隊
if (leftNode != null) {
queue.enquene(leftNode);
}
if (rightNode != null) {
queue.enquene(rightNode);
}
node = queue.dequeue();//遍歷下個節(jié)點
}
return sb.toString();
}
@Override
public T findMin() {
return findMin(mRoot).data;
}
@Override
public T findMax() {
return findMax(mRoot).data;
}
@Override
public BinaryNode findNode(T data) {
//@see SearchTree中的實現(xiàn)
//return findNode(T data, mRoot)
return null;
}
/**
* 遞歸查找元素
*
* @param data
* @param currentNode
* @return
*/
public AVLNode findNode(T data, AVLNode<T> currentNode) {
if (currentNode == null) {
return null;
}
int compareResult = data.compareTo(currentNode.data);
if (compareResult == 0) {
return currentNode;
}
if (compareResult > 0) {
currentNode = findNode(data, currentNode.right);//移動node指針
} else if (compareResult < 0) {
currentNode = findNode(data, currentNode.left);
}
return currentNode;
}
@Override
public boolean contains(T data) {
return contains(data, mRoot);
}
/**
* 遞歸實現(xiàn)包含
*
* @param data
* @param tree
* @return
*/
public boolean contains(T data, AVLNode<T> tree) {
if (data == null) {
return false;
}
if (tree == null) {
return false;
}
int compareResult = data.compareTo(tree.data);
if (compareResult == 0) {
return true;
} else if (compareResult > 0) {
return contains(data, tree.right);
} else if (compareResult < 0) {
return contains(data, tree.left);
}
return false;
}
@Override
public void clear() {
mRoot = null;
}
}
最后獻上測試代碼:
@Test
public void testAVLTree() {
Integer[] avlArr = {9, 8, 7, 6, 5, 4, 3, 2, 1};
AVLTree<Integer> avlTree = new AVLTree<>();
for (int i = 0; i < avlArr.length; i++) {
avlTree.insert(avlArr[i]);
}
System.out.println("=====平衡二叉樹輸出=====");
dumpAvlTree(avlTree.mRoot);//樹結(jié)構(gòu)打印測試
avlTree.remove(6);
System.out.println("=====平衡二叉樹深度=====");
System.out.println(avlTree.height());
System.out.println("=====平衡二叉樹大小=====");
System.out.println(avlTree.size());
System.out.println("=====平衡二叉樹刪除元素后輸出=====");
dumpAvlTree(avlTree.mRoot);
System.out.println("=====平衡二叉樹深度=====");
System.out.println(avlTree.height());
}
/**
* 打印AVL樹信息
*
* @param root
*/
public void dumpAvlTree(AVLNode<Integer> root) {
if (root == null) {
return;
}
StringBuilder sb = new StringBuilder();
sb.append(root.data);
sb.append("(");
if (root.left != null && root.right != null) {
sb.append(root.left.data + "-" + root.left.height);
sb.append(" , ");
sb.append(root.right.data + "-" + root.right.height);
} else if (root.left != null) {
sb.append(root.left.data + "-" + root.left.height);
sb.append(" , ");
} else if (root.right != null) {
sb.append(" , ");
sb.append(root.right.data + "-" + root.right.height);
}
sb.append(")");
System.out.println(sb.toString());
dumpAvlTree(root.left);
dumpAvlTree(root.right);
}
測試結(jié)果如下:
完整代碼地址:數(shù)據(jù)結(jié)構(gòu)與算法學(xué)習(xí)JAVA描述GayHub地址