數據結構之平衡二叉樹及Java實現

一、平衡二叉樹的基本介紹

平衡二叉樹是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。構造與調整方法平衡二叉樹的常用算法有紅黑樹、AVL、Treap等。最小二叉平衡樹的節點的公式如下 F(n)=F(n-1)+F(n-2)+1 這個類似於一個遞歸的數列,可以參考Fibonacci數列,1是根節點,F(n-1)是左子樹的節點數量,F(n-2)是右子樹的節點數量。

平衡二叉樹是對二叉排序樹的優化,防止二叉排序樹在最壞情況(插入順序恰好是有序的)下平均查找時間爲n,二叉排序樹在此時形如一個單鏈表,而平衡二叉樹查找元素的次數不超過樹的深度,時間複雜度爲logN 。

二、平衡二叉樹的實現

參考資料——java數據結構與算法之平衡二叉樹(AVL樹)的設計與實現

實現代碼:


public class AVLTree<T extends Comparable>{


    class Node<T>{
        private T data = null;
        int height = 0;
        private Node left;
        private Node right;

        public Node(T data){
            this.data = data;
        }
        public Node(Node<T> left, Node<T> right, T data) {
            this(left,right,data,0);
        }

        public Node(Node<T> left, Node<T> right, T data, int height) {
            this.left=left;
            this.right=right;
            this.data=data;
            this.height = height;
        }
    }
    /** The tree root. */
    public Node<T> root;


    public boolean isEmpty() {
        return root==null;
    }

    public int size() {
        return size(root);
    }

    @SuppressWarnings("unchecked")
    public int size(Node<T> subtree){
        if(subtree==null){
            return 0;
        }else {
            return size(subtree.left) + 1 + size(subtree.right);
        }
    }

    public int height() {
        return height(root);
    }


    /**
     * @param p
     * @return
     */
    public int height(Node<T> p){
        return p == null ? -1 : p.height;
    }



    public String preOrder() {
        String sb=preOrder(root);
        if(sb.length()>0){
            //去掉尾部","號
            sb=sb.substring(0,sb.length()-1);
        }
        return sb;
    }

    /**
     * 先根遍歷
     * @param subtree
     * @return
     */
    public String preOrder(Node<T> subtree){
        StringBuilder sb =new StringBuilder();
        if (subtree!=null) {
            //先訪問根結點
            sb.append(subtree.data).append(",");
            //訪問左子樹
            sb.append(preOrder(subtree.left));
            //訪問右子樹
            sb.append(preOrder(subtree.right));
        }
        return sb.toString();
    }


    public String inOrder() {
        String sb=inOrder(root);
        if(sb.length()>0){
            //去掉尾部","號
            sb=sb.substring(0,sb.length()-1);
        }
        return sb;
    }

    /**
     * 中根遍歷
     * @param subtree
     * @return
     */
    private String inOrder(Node<T> subtree){
        StringBuilder sb =new StringBuilder();
        if (subtree!=null) {
            //訪問左子樹
            sb.append(inOrder(subtree.left));
            //訪問根結點
            sb.append(subtree.data).append(",");
            //訪問右子樹
            sb.append(inOrder(subtree.right));
        }
        return sb.toString();
    }

    public String postOrder() {
        String sb=postOrder(root);
        if(sb.length()>0){
            //去掉尾部","號
            sb=sb.substring(0,sb.length()-1);
        }
        return sb;
    }

    /**
     * 後根遍歷
     * @param subtree
     * @return
     */
    private String postOrder(Node<T> subtree){
        StringBuilder sb =new StringBuilder();
        if (subtree!=null){
            //訪問左子樹
            sb.append(postOrder(subtree.left));
            //訪問右子樹
            sb.append(postOrder(subtree.right));
            //訪問根結點
            sb.append(subtree.data).append(",");
        }
        return sb.toString();
    }


    /**
     * 插入方法
     * @param data
     */
    public void insert(T data) {
        if (data==null){
            throw new RuntimeException("data can\'t not be null ");
        }
        this.root=insert(data,root);
    }

    private Node<T> insert(T data , Node<T> p){

        //說明已沒有孩子結點,可以創建新結點插入了.
        if(p==null){
            p=new Node<T>(data);
        }

        int result=data.compareTo(p.data);

        if(result<0){//向左子樹尋找插入位置
            p.left=insert(data,p.left);

            //插入後計算子樹的高度,等於2則需要重新恢復平衡,由於是左邊插入,左子樹的高度肯定大於等於右子樹的高度
            if(height(p.left)-height(p.right)==2){
                //判斷data是插入點的左孩子還是右孩子
                if(data.compareTo(p.left.data)<0){
                    //進行LL旋轉
                    p=singleRotateLeft(p);
                }else {
                    //進行左右旋轉
                    p=doubleRotateWithLeft(p);
                }
            }
        }else if (result>0){//向右子樹尋找插入位置
            p.right=insert(data,p.right);

            if(height(p.right)-height(p.left)==2){
                if (data.compareTo(p.right.data)<0){
                    //進行右左旋轉
                    p=doubleRotateWithRight(p);
                }else {
                    //進行RR旋轉
                    p=singleRotateRight(p);
                }
            }
        }
        else
            ;//if exist do nothing
        //重新計算各個結點的高度
        p.height = Math.max( height( p.left ), height( p.right ) ) + 1;

        return p;//返回根結點
    }


    /**
     * 刪除方法
     * @param data
     */
    public void remove(T data) {
        if (data==null){
            throw new RuntimeException("data can\'t not be null ");
        }
        this.root=remove(data,root);
    }

    /**
     * 刪除操作
     * @param data
     * @param p
     * @return
     */
    private Node<T> remove(T data,Node<T> p){

        if(p ==null)
            return null;

        int result=data.compareTo(p.data);

        //從左子樹查找需要刪除的元素
        if(result<0){
            p.left=remove(data,p.left);

            //檢測是否平衡
            if(height(p.right)-height(p.left)==2){
                Node<T> currentNode=p.right;
                //判斷需要那種旋轉
                if(height(currentNode.left)>height(currentNode.right)){
                    //RL
                    p=doubleRotateWithRight(p);
                }else{
                    //RR
                    p=singleRotateRight(p);
                }
            }

        }
        //從右子樹查找需要刪除的元素
        else if(result>0){
            p.right=remove(data,p.right);
            //檢測是否平衡
            if(height(p.left)-height(p.right)==2){
                Node<T> currentNode=p.left;
                //判斷需要那種旋轉
                if(height(currentNode.right)>height(currentNode.left)){
                    //LR
                    p=doubleRotateWithLeft(p);
                }else{
                    //LL
                    p=singleRotateLeft(p);
                }
            }
        }
        //已找到需要刪除的元素,並且要刪除的結點擁有兩個子節點
        else if(p.right!=null&&p.left!=null){

            //尋找替換結點
            p.data=findMin(p.right).data;

            //移除用於替換的結點
            p.right = remove( p.data, p.right );
        }
        else {
            //只有一個孩子結點或者只是葉子結點的情況
            p=(p.left!=null)? p.left:p.right;
        }

        //更新高度值
        if(p!=null)
            p.height = Math.max( height( p.left ), height( p.right ) ) + 1;
        return p;
    }


    /**
     * 查找最小值結點
     * @param p
     * @return
     */
    private Node<T> findMin(Node<T> p){
        if (p==null)//結束條件
            return null;
        else if (p.left==null)//如果沒有左結點,那麼t就是最小的
            return p;
        return findMin(p.left);
    }

    public T findMin() {
        return findMin(root).data;
    }

    public T findMax() {
        return findMax(root).data;
    }

    /**
     * 查找最大值結點
     * @param p
     * @return
     */
    private Node<T> findMax(Node<T> p){
        if (p==null)
            return null;
        else if (p.right==null)//如果沒有右結點,那麼t就是最大的
            return p;
        return findMax(p.right);
    }



    public boolean contains(T data) {
        return data != null && contain(data, root);
    }

    public boolean contain(T data , Node<T> subtree){

        if (subtree==null)
            return false;

        int result =data.compareTo(subtree.data);

        if (result<0){
            return contain(data,subtree.left);
        }else if(result>0){
            return contain(data,subtree.right);
        }else {
            return true;
        }
    }

    public void clear() {
       this.root=null;
    }

    /**
     * 左左單旋轉(LL旋轉) w變爲x的根結點, x變爲w的右子樹
     * @param x
     * @return
     */
    private Node<T> singleRotateLeft(Node<T> x){
        //把w結點旋轉爲根結點
        Node<T> w=  x.left;
        //同時w的右子樹變爲x的左子樹
        x.left=w.right;
        //x變爲w的右子樹
        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旋轉) x變爲w的根結點, w變爲x的左子樹
     * @return
     */
    private Node<T> singleRotateRight(Node<T> w){

        Node<T> x=w.right;

        w.right=x.left;
        x.left=w;

        //重新計算x/w的高度
        x.height=Math.max(height(x.left),w.height)+1;
        w.height=Math.max(height(w.left),height(w.right))+1;

        //返回新的根結點
        return x;
    }

    /**
     * 左右旋轉(LR旋轉) x(根) w y 結點 把y變成根結點
     * @return
     */
    private Node<T> doubleRotateWithLeft(Node<T> x){
        //w先進行RR旋轉
        x.left=singleRotateRight(x.left);
        //再進行x的LL旋轉
        return singleRotateLeft(x);
    }

    /**
     * 右左旋轉(RL旋轉)
     * @param w
     * @return
     */
    private Node<T> doubleRotateWithRight(Node<T> w){
        //先進行LL旋轉
        w.right=singleRotateLeft(w.right);
        //再進行RR旋轉
        return singleRotateRight(w);
    }


    private void printTree( Node<T> t )
    {
        if( t != null )
        {
            printTree( t.left );
            System.out.println( t.data );
            printTree( t.right );
        }
    }


    /**
     * 測試
     * @param arg
     */
    public  static void main(String arg[]){

        AVLTree<Integer> avlTree=new AVLTree<Integer>();

        for (int i = 1; i <18 ; i++) {
            avlTree.insert(i);
        }

        avlTree.printTree(avlTree.root);
        //刪除11,8以觸發旋轉平衡操作
        avlTree.remove(11);
        avlTree.remove(8);

        System.out.println("================");

        avlTree.printTree(avlTree.root);
        System.out.println("findMin:"+avlTree.findMin());
        System.out.println("findMax:"+avlTree.findMax());
        System.out.println("15 exist or not : " + avlTree.contains(15) );
        System.out.println("先根遍歷:"+avlTree.preOrder());
        System.out.println("中根遍歷:"+avlTree.inOrder());
        System.out.println("後根遍歷:"+avlTree.postOrder());

    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章