package com.utils;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* 平衡二叉樹
* 定義:首先它是一種特殊的二叉排序樹,其次它的左子樹和右子樹都是平衡二叉樹,
* 且左子樹和右子樹的深度之差不超過1
* 平衡因子:可以定義為左子樹的深度減去右子樹的深度
*
* 平衡二叉樹是對二叉排序樹的優化,防止二叉排序樹在最壞情況下平均查找時間為n,
* 二叉排序樹在此時形如一個單鏈表
* 平衡二叉樹查找元素的次數不超過樹的深度,時間復雜度為logN
*/
public class AVLTree<E> {
/**
* 根節點
*/
private Entry<E> root = null;
/**
* 樹中元素個數
*/
private int size = 0;
public AVLTree(){
}
public int size(){
return size;
}
/**
* @param p 最小旋轉子樹的根節點
* 向左旋轉之后p移到p的左子樹處,p的右子樹B變為此最小子樹根節點,
* B的左子樹變為p的右子樹
* 比如: A(-2) B(1)
* \ 左旋轉 / \
* B(0) ----> A(0) \
* / \ \ \
* BL(0) BR(0) BL(0) BR(0)
* 旋轉之后樹的深度之差不超過1
*/
private void rotateLeft(Entry<E> p) {
System.out.println("繞"+p.element+"左旋");
if(p!=null){
Entry<E> r = p.right;
p.right = r.left; //把p右子樹的左節點嫁接到p的右節點,如上圖,把BL作為A的右子節點
if (r.left != null) //如果B的左節點BL不為空,把BL的父節點設為A
r.left.parent = p;
r.parent = p.parent; //A的父節點設為B的父節點
if (p.parent == null) //如果p是根節點
root = r; //r變為父節點,即B為父節點
else if (p.parent.left == p) //如果p是左子節點
p.parent.left = r; //p的父節點的左子樹為r
else //如果p是右子節點
p.parent.right = r; //p的父節點的右子樹為r
r.left = p; //p變為r的左子樹,即A為B的左子樹
p.parent = r; //同時更改p的父節點為r,即A的父節點為B
}
}
/**
* @param p 最小旋轉子樹的根節點
* 向右旋轉之后,p移到p的右子節點處,p的左子樹B變為最小旋轉子樹的根節點
* B的右子節點變為p的左節點、
* 例如: A(2) B(-1)
* / 右旋轉 / \
* B(0) ------> / A(0)
* / \ / /
* BL(0) BR(0) BL(0) BR(0)
*/
private void rotateRight(Entry<E> p){
System.out.println("繞"+p.element+"右旋");
if(p!=null){
Entry<E> l = p.left;
p.left = l.right; //把B的右節點BR作為A的左節點
if (l.right != null) //如果BR不為null,設置BR的父節點為A
l.right.parent = p;
l.parent = p.parent; //A的父節點賦給B的父節點
if (p.parent == null) //如果p是根節點
root = l; //B為根節點
else if (p.parent.right == p) //如果A是其父節點的左子節點
p.parent.right = l; //B為A的父節點的左子樹
else //如果A是其父節點的右子節點
p.parent.left = l; //B為A的父節點的右子樹
l.right = p; //A為B的右子樹
p.parent = l; //設置A的父節點為B
}
}
/**
* 平衡而二叉樹的插入操作
* 基本原理為:
* 1.首先如同二叉排序樹一般,找到其要插入的節點的位置,并把元素插入其中;
* 2.自下向上進行回溯,回溯做兩個事情:
* (1)其一是修改祖先節點的平衡因子,當插入 一個節點時只有根節點到插入節點
* 的路徑中的節點的平衡因子會被改變,而且改變的原則是當插入節點在某節點(稱為A)
* 的左子樹 中時,A的平衡因子(稱為BF)為BF+1,當插入節點在A的右子樹中時A的BF-1,
* 而判斷插入節點在左子樹中還是右子樹中只要簡單的比較它與A的大小
* (2)在改變祖先節點的平衡因子的同時,找到最近一個平衡因子大于2或小于-2的節點,
* 從這個節點開始調整最小不平衡樹進行旋轉調整,關于如何調整見下文。
* 由于調整后,最小不平衡子樹的高度與插入節點前的高度相同,故不需繼續要調整祖先節點。
* 這里還有一個特殊情況,如果調整BF時,發現某個節點的BF變為0了,則停止向上繼續調整,
* 因為這表明此節點中高度小的子樹增加了新節點,高度不變,那么祖先節點的BF自然不變。
*
*
*/
public boolean add(E element){
Entry<E> t = root;
if(t == null){
root = new Entry<E>(element,null);
size = 1;
return true;
}
int cmp;
Entry<E> parent; //保存t的父節點
Comparable<? super E> e = (Comparable<? super E>) element;
//從根節點向下搜索,找到插入位置
do {
parent = t;
cmp = e.compareTo(t.element);
if(cmp < 0){
t = t.left;
}else if(cmp > 0){
t = t.right;
}else{
return false;
}
} while (t!=null);
Entry<E> child = new Entry(element,parent);
if(cmp < 0){
parent.left = child;
}else{
parent.right = child;
}
//自下向上回溯,查找最近不平衡節點
while(parent!=null){
cmp = e.compareTo(parent.element);
if(cmp < 0){ //插入節點在parent的左子樹中
parent.balance++;
}else{ //插入節點在parent的右子樹中
parent.balance--;
}
if(parent.balance == 0){ //此節點的balance為0,不再向上調整BF值,且不需要旋轉
break;
}
if(Math.abs(parent.balance) == 2){ //找到最小不平衡子樹根節點
fixAfterInsertion(parent);
break; //不用繼續向上回溯
}
parent = parent.parent;
}
size ++;
return true;
}
/**
* 調整的方法:
* 1.當最小不平衡子樹的根(以下簡稱R)為2時,即左子樹高于右子樹:
* 如果R的左子樹的根節點的BF為1時,做右旋;
* 如果R的左子樹的根節點的BF為-1時,先左旋然后再右旋
*
* 2.R為-2時,即右子樹高于左子樹:
* 如果R的右子樹的根節點的BF為1時,先右旋后左旋
* 如果R的右子樹的根節點的BF為-1時,做左旋
*
* 至于調整之后,各節點的BF變化見代碼
*/
private void fixAfterInsertion(Entry<E> p){
if(p.balance == 2){
leftBalance(p);
}
if(p.balance == -2){
rightBalance(p);
}
}
/**
* 做左平衡處理
* 平衡因子的調整如圖:
* t rd
* / \ / \
* l tr 左旋后右旋 l t
* / \ -------> / \ / \
* ll rd ll rdl rdr tr
* / \
* rdl rdr
*
* 情況2(rd的BF為0)
*
*
* t rd
* / \ / \
* l tr 左旋后右旋 l t
* / \ -------> / \ \
* ll rd ll rdl tr
* /
* rdl
*
* 情況1(rd的BF為1)
*
*
* t rd
* / \ / \
* l tr 左旋后右旋 l t
* / \ -------> / / \
* ll rd ll rdr tr
* \
* rdr
*
* 情況3(rd的BF為-1)
*
*
* t l
* / 右旋處理 / \
* l ------> ll t
* / \ /
* ll rd rd
* 情況4(L等高)
*/
private boolean leftBalance(Entry<E> t){
boolean heightLower = true;
Entry<E> l = t.left;
switch (l.balance) {
case LH: //左高,右旋調整,旋轉后樹的高度減小
t.balance = l.balance = EH;
rotateRight(t);
break;
case RH: //右高,分情況調整
Entry<E> rd = l.right;
switch (rd.balance) { //調整各個節點的BF
case LH: //情況1
t.balance = RH;
l.balance = EH;
break;
case EH: //情況2
t.balance = l.balance = EH;
break;
case RH: //情況3
t.balance = EH;
l.balance = LH;
break;
}
rd.balance = EH;
rotateLeft(t.left);
rotateRight(t);
break;
case EH: //特殊情況4,這種情況在添加時不可能出現,只在移除時可能出現,旋轉之后整體樹高不變
l.balance = RH;
t.balance = LH;
rotateRight(t);
heightLower = false;
break;
}
return heightLower;
}
/**
* 做右平衡處理
* 平衡因子的調整如圖:
* t ld
* / \ / \
* tl r 先右旋再左旋 t r
* / \ --------> / \ / \
* ld rr tl ldl ldr rr
* / \
* ldl ldr
* 情況2(ld的BF為0)
*
* t ld
* / \ / \
* tl r 先右旋再左旋 t r
* / \ --------> / \ \
* ld rr tl ldl rr
* /
* ldl
* 情況1(ld的BF為1)
*
* t ld
* / \ / \
* tl r 先右旋再左旋 t r
* / \ --------> / / \
* ld rr tl ldr rr
* \
* ldr
* 情況3(ld的BF為-1)
*
* t r
* \ 左旋 / \
* r -------> t rr
* / \ \
* ld rr ld
* 情況4(r的BF為0)
*/
private boolean rightBalance(Entry<E> t){
boolean heightLower = true;
Entry<E> r = t.right;
switch (r.balance) {
case LH: //左高,分情況調整
Entry<E> ld = r.left;
switch (ld.balance) { //調整各個節點的BF
case LH: //情況1
t.balance = EH;
r.balance = RH;
break;
case EH: //情況2
t.balance = r.balance = EH;
break;
case RH: //情況3
t.balance = LH;
r.balance = EH;
break;
}
ld.balance = EH;
rotateRight(t.right);
rotateLeft(t);
break;
case RH: //右高,左旋調整
t.balance = r.balance = EH;
rotateLeft(t);
break;
case EH: //特殊情況4
r.balance = LH;
t.balance = RH;
rotateLeft(t);
heightLower = false;
break;
}
return heightLower;
}
/**
* 查找指定元素,如果找到返回其Entry對象,否則返回null
*/
private Entry<E> getEntry(Object element) {
Entry<E> tmp = root;
Comparable<? super E> e = (Comparable<? super E>) element;
int c;
while (tmp != null) {
c = e.compareTo(tmp.element);
if (c == 0) {
return tmp;
} else if (c < 0) {
tmp = tmp.left;
} else {
tmp = tmp.right;
}
}
return null;
}
/**
* 平衡二叉樹的移除元素操作
*
*/
public boolean remove(Object o){
Entry<E> e = getEntry(o);
if(e!=null){
deleteEntry(e);
return true;
}
return false;
}
private void deleteEntry(Entry<E> p){
size --;
//如果p左右子樹都不為空,找到其直接后繼,替換p,之后p指向s,刪除p其實是刪除s
//所有的刪除左右子樹不為空的節點都可以調整為刪除左右子樹有其一不為空,或都為空的情況。
if (p.left != null && p.right != null) {
Entry<E> s = successor(p);
p.element = s.element;
p = s;
}
Entry<E> replacement = (p.left != null ? p.left : p.right);
if (replacement != null) { //如果其左右子樹有其一不為空
replacement.parent = p.parent;
if (p.parent == null) //如果p為root節點
root = replacement;
else if (p == p.parent.left) //如果p是左孩子
p.parent.left = replacement;
else //如果p是右孩子
p.parent.right = replacement;
p.left = p.right = p.parent = null; //p的指針清空
//這里更改了replacement的父節點,所以可以直接從它開始向上回溯
fixAfterDeletion(replacement);
} else if (p.parent == null) { // 如果全樹只有一個節點
root = null;
} else { //左右子樹都為空
fixAfterDeletion(p); //這里從p開始回溯
if (p.parent != null) {
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}
/**
* 返回以中序遍歷方式遍歷樹時,t的直接后繼
*/
static <E> Entry<E> successor(Entry<E> t) {
if (t == null)
return null;
else if (t.right != null) { //往右,然后向左直到盡頭
Entry<E> p = t.right;
while (p.left != null)
p = p.left;
return p;
} else { //right為空,如果t是p的左子樹,則p為t的直接后繼
Entry<E> p = t.parent;
Entry<E> ch = t;
while (p != null && ch == p.right) { //如果t是p的右子樹,則繼續向上搜索其直接后繼
ch = p;
p = p.parent;
}
return p;
}
}
/**
* 刪除某節點p后的調整方法:
* 1.從p開始向上回溯,修改祖先的BF值,這里只要調整從p的父節點到根節點的BF值,
* 調整原則為,當p位于某祖先節點(簡稱A)的左子樹中時,A的BF減1,當p位于A的
* 右子樹中時A的BF加1。當某個祖先節點BF變為1或-1時停止回溯,這里與插入是相反的,
* 因為原本這個節點是平衡的,刪除它的子樹的某個節點并不會改變它的高度
*
* 2.檢查每個節點的BF值,如果為2或-2需要進行旋轉調整,調整方法如下文,
* 如果調整之后這個最小子樹的高度降低了,那么必須繼續從這個最小子樹的根節點(假設為B)繼續
* 向上回溯,這里和插入不一樣,因為B的父節點的平衡性因為其子樹B的高度的改變而發生了改變,
* 那么就可能需要調整,所以刪除可能進行多次的調整。
*
*/
private void fixAfterDeletion(Entry<E> p){
boolean heightLower = true; //看最小子樹調整后,它的高度是否發生變化,如果減小,繼續回溯
Entry<E> t = p.parent;
Comparable<? super E> e = (Comparable<? super E>)p.element;
int cmp;
//自下向上回溯,查找不平衡的節點進行調整
while(t!=null && heightLower){
cmp = e.compareTo(t.element);
/**
* 刪除的節點是右子樹,等于的話,必然是刪除的某個節點的左右子樹不為空的情況
* 例如: 10
* / \
* 5 15
* / \
* 3 6
* 這里刪除5,是把6的值賦給5,然后刪除6,這里6是p,p的父節點的值也是6。
* 而這也是右子樹的一種
*/
if(cmp >= 0 ){
t.balance ++;
}else{
t.balance --;
}
if(Math.abs(t.balance) == 1){ //父節點經過調整平衡因子后,如果為1或-1,說明調整之前是0,停止回溯。
break;
}
Entry<E> r = t;
//這里的調整跟插入一樣
if(t.balance == 2){
heightLower = leftBalance(r);
}else if(t.balance==-2){
heightLower = rightBalance(r);
}
t = t.parent;
}
}
private static final int LH = 1; //左高
private static final int EH = 0; //等高
private static final int RH = -1; //右高
/**
* 樹節點,為方便起見不寫get,Set方法
*/
static class Entry<E>{
E element;
Entry<E> parent;
Entry<E> left;
Entry<E> right;
int balance = EH; //平衡因子,只能為1或0或-1
public Entry(E element,Entry<E> parent){
this.element = element;
this.parent = parent;
}
public Entry(){}
@Override
public String toString() {
return element+" BF="+balance;
}
}
//返回中序遍歷此樹的迭代器,返回的是一個有序列表
private class BinarySortIterator implements Iterator<E>{
Entry<E> next;
Entry<E> lastReturned;
public BinarySortIterator(){
Entry<E> s = root;
if(s !=null){
while(s.left != null){ //找到中序遍歷的第一個元素
s = s.left;
}
}
next = s; //賦給next
}
@Override
public boolean hasNext() {
return next!=null;
}
@Override
public E next() {
Entry<E> e = next;
if (e == null)
throw new NoSuchElementException();
next = successor(e);
lastReturned = e;
return e.element;
}
@Override
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
// deleted entries are replaced by their successors
if (lastReturned.left != null && lastReturned.right != null)
next = lastReturned;
deleteEntry(lastReturned);
lastReturned = null;
}
}
public Iterator<E> itrator(){
return new BinarySortIterator();
}
private int treeHeight(Entry<E> p){
if(p == null){
return -1;
}
return 1 + Math.max(treeHeight(p.left), treeHeight(p.right));
}
//測試,你也可以任意測試
public static void main(String[] args) {
AVLTree<Integer> tree = new AVLTree<Integer>();
System.out.println("------添加------");
tree.add(50);
System.out.print(50+" ");
tree.add(66);
System.out.print(66+" ");
for(int i=0;i<10;i++){
int ran = (int)(Math.random() * 100);
System.out.print(ran+" ");
tree.add(ran);
}
System.out.println("------刪除------");
tree.remove(50);
tree.remove(66);
System.out.println();
Iterator<Integer> it = tree.itrator();
while(it.hasNext()){
System.out.print(it.next()+" ");
}
}
}
平衡二叉樹 java實現
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
- 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
- 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
- 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
推薦閱讀更多精彩內容
- 最近看了一下大學的數據結構,??學到了以前沒學到的東西看到了二叉樹那一塊,感覺二叉樹是個很重要的東西,就看了一下底層...
- Tree 樹是一種數據結構,由n(>=0)個有限節點Node組成的一個具有層次關系的集合。 樹的特點 每個子節點都...
- 1. 樹結構示意圖 補充: 兄弟節點:具有相同父節點的節點互稱為兄弟節點。 樹的深度:從根節點開始(其深度為0)自...
- 引言 前兩篇文章中,我們學習了二叉樹的結構、性質、實現以及應用,它的操作時間復雜度為O(lgn),但注意這個復雜度...