java实现平衡二叉树(详细分析)
生活随笔
收集整理的這篇文章主要介紹了
java实现平衡二叉树(详细分析)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
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的父節點設為Ar.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的父節點的左子樹為relse //如果p是右子節點p.parent.right = r; //p的父節點的右子樹為rr.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的父節點為Al.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) { //調整各個節點的BFcase LH: //情況1t.balance = RH;l.balance = EH;break;case EH: //情況2t.balance = l.balance = EH;break;case RH: //情況3t.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) { //調整各個節點的BFcase LH: //情況1t.balance = EH;r.balance = RH;break;case EH: //情況2t.balance = r.balance = EH;break;case RH: //情況3t.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: //特殊情況4r.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或-1public Entry(E element,Entry<E> parent){this.element = element;this.parent = parent;}public Entry(){}@Overridepublic 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}@Overridepublic boolean hasNext() {return next!=null;}@Overridepublic E next() {Entry<E> e = next;if (e == null)throw new NoSuchElementException();next = successor(e);lastReturned = e;return e.element;}@Overridepublic void remove() {if (lastReturned == null)throw new IllegalStateException();// deleted entries are replaced by their successorsif (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实现平衡二叉树(详细分析)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java二叉树的运用
- 下一篇: 二叉排序树的实现——java