平衡二叉樹 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的父節點設為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()+" ");
        }
    }
}
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容