Java資料結構與排序演算法 (二)

Tongson發表於2018-08-05

回顧

前面學習了一些資料結構,根據其實現方式,這些資料結構可以劃分為兩種型別:基於陣列的實現與基於連結串列的實現。

正如我們已經看到的,就其效率而言,這兩種實現方式各有長短

具體來說,基於陣列實現的結構允許我們通過下標或秩,在常數的時間內找到目標物件,並讀取或更新其內容。然而,一旦需要對這類結構進行修改,那麼無論是插入還是刪除,都需要耗費線性的時間。

反過來,基於連結串列實現的結構允許我們藉助引用或位置物件,在常數的時間內插入或刪除元素;但是為了找出居於特定次序的元素,我們不得不花費線性的時間對整個結構進行遍歷查詢。

能否將這兩類結構的優點結合起來,並回避其不足呢?接下來將要介紹的結構,將正面回答這一問題。

線性結構(Linear structures)、非線性結構(Non-linear structures )、半線性結構(Semi-linear structures)。

“樹的某一元素是另一元 素‘直接上鄰’”,也可以說“某一元素是另一元素的‘直接下鄰’之一”。

術語

節點(Node)的深度、樹的深度depth(v)與高度、樹根(Root)、父 親(Parent)、孩子(Child)、樹邊(Edge)、“兄弟” (Sibling)、“度”(Degree)、“內部節點”(Internal node)、“外部節點”(External node)、“葉子”(Leaf)、路徑(Path)、“祖先”(Ancestor)、“後代”(Descendent)、子樹(Subtree)、“空樹”(Empty tree)、共同祖先(Common ancestor)、最低共同祖先(Lowerest common ancestor)、“有 序樹(Ordered tree)”、二叉樹(Binary tree)、真二叉樹(Proper bina ry tree )、非真二叉樹 (Improper binary tree)、完全二叉樹(Complete binary tree)

/*
> 節點的深度、樹的深度與高度

定義.1 在樹結構中,
b 每個節點的深度都是一個非負整數;
c 深度為 0 的節點有且僅有一個,稱作樹根(Root);
d 對於深度為 k (k≥1)的每個節點 u,都有且僅有一個深度為 k-1 的節點 v 與之對應,稱作 u 的父親(Parent)或父節點。

定義.2 若節點 v 是節點 u 的父親,則 u 稱作 v 的孩子(Child),並在二者之間建立一條樹邊(Edge)。

定義.3 樹中所有節點的最大深度,稱作樹的深度或高度。

觀察結論.1 樹中節點的數目,總是等於邊數加一。

> 度、內部節點與外部節點

定義.4 任一節點的孩子數目,稱作它的“度”(Degree)。

定義.5 至少擁有一個孩子的節點稱作“內部節點”(Internal node);沒有任何孩子的節點則稱作“外部節點”(External node)或“葉子”(Leaf)。

定義.6 由樹中 k+1 節點通過樹邊首尾銜接而構成的序列{ (v0, v1), (v1, v2), …, (vk-1, vk) | k ≥ 0},稱作樹中長度為 k 的一條路徑(Path)。

觀察結論.2 樹中任何兩個節點之間都存在唯一的一條路徑。

觀察結論.3 若 v 是 u 的父親,則 depth(v) + 1 = depth(u)。

推論.1 從樹根通往任一節點的路徑長度,恰好等於該節點的深度。

> 祖先、後代、子樹和節點的高度

定義.7
0. 每個節點都是自己的“祖先”(Ancestor),也是自己的“後代”(Descendent);
1. 若 v 是 u 的父節點的祖先,則 v 也是 u 的祖先;
2. 若 u 的父節點是 v 的後代,則 u 也是 v 的後代。

定義.8 除節點本身以外的祖先(後代),稱作真祖先(後代)。

觀察結論.4 任一節點 v 的深度,等於其真祖先的數目。

觀察結論.5 任一節點 v 的祖先,在每一深度上最多隻有一個。

定義.9 樹 T 中每一節點 v 的所有後代也構成一棵樹,稱作 T 的“以 v 為根的子樹(Subtree)”。

定義.10 若子樹 v 的深度(高度)為 h,則稱 v 的高度為 h,記作 height(v) = h。

觀察結論.6 對於葉子節點 u 的任何祖先 v,必有 depth(v) + height(v) ≥ depth(u)。

> 共同祖先及最低共同祖先

定義.11 在樹 T 中,若節點 u 和 v 都是節點 a 的後代,則稱節點 a 為節點 u 和 v 的共同祖先(Commonancestor)。

定義.11 在樹 T 中,若節點 u 和 v 都是節點 a 的後代,則稱節點 a 為節點 u 和 v 的共同祖先(Commonancestor)。

觀察結論.7 每一對節點至少存在一個共同祖先。

定義.12 在一對節點 u 和 v 的所有共同祖先中,深度最大者稱為它們的最低共同祖先(Lowerestcommon ancestor),記作 lca(u, v)。

觀察結論.8 每一對節點的最低共同祖先必存在且唯一。

> 有序樹、m 叉樹

定義.13 在樹 T 中,若在每個節點的所有孩子之間都可以定義某一線性次序,則稱 T 為一棵“有序樹(Ordered tree)”。

定義.14 每個內部節點均為 m 度的有序樹,稱作 m 叉樹。

二叉樹>

定義.15 每個節點均不超過 2 度的有序樹,稱作二叉樹(Binary tree)。

定義.16 不含 1 度節點的二叉樹,稱作真二叉樹(Proper bina ry tree ),否則稱作非真二叉樹(Improper binary tree)。

觀察結論.9 在二叉樹中,深度為 k 的節點不超過 2k 個。

推論.2 高度為 h 的二叉樹最多包含 2h+1-1 個節點。

推論.3 由 n 個節點構成的二叉樹,高度至少為⎣log2n⎦。

觀察結論.10 在二叉樹中,葉子總是比 2 度節點多一個。

> 滿二叉樹與完全二叉樹

定義.17 若二叉樹 T 中所有葉子的深度完全相同,則稱之為滿二叉樹(Full binary tree)。

觀察結論.11 高度為 h 的二叉樹是滿的,當且僅當它擁有 2h匹葉子、2h+1-1 個節點。

定義.18 若在一棵滿二叉樹中,從最右側起將相鄰的若干匹葉子節點摘除掉,則得到的二叉樹稱作完全二叉樹(Complete binary tree)。

引理.1 由 n 個節點構成的完全二叉樹,高度 h = ⎣log2n⎦。

推論.4 在由固定數目的節點所組成的所有二叉樹中,完全二叉樹的高度最低。

> 樹的基本演算法
getSize()⎯⎯統計(子)樹的規模

觀察結論四.12 一棵樹的規模,等於根節點下所有子樹規模之和再加一,也等於根節點的後代總數。

getHeight()⎯⎯計算節點的高度

推論四.5 c 若 u 是 v 的孩子,則 height(v) ≥ height(u) + 1;
 d height(v) = 1 + max height(u)。
                    u是v的孩子

推論四.6 若 u 是 v 的孩子,則 depth(u) = depth(v) + 1。

定理四.1 樹的前序、後序及層次遍歷,均可在 O(n)時間內完成,其中 n 為樹本身的規模。
*/
複製程式碼

樹抽象資料型別及其實現

public interface Tree {

    /**
     * @return 當前節點中存放的物件
     */
    Object getElem();

    /**
     * 將物件obj存入當前節點,並返回此前的內容
     *
     * @param obj
     * @return
     */
    Object setElem(Object obj);

    /**
     * @return 當前節點的父節點
     */
    TreeLinkedList getParent();

    /**
     * @return 當前節點的長子
     */
    TreeLinkedList getFirstChild();

    /**
     * @return 當前節點的最大弟弟
     */
    TreeLinkedList getNextSibling();

    /**
     * @return 當前節點後代元素的數目,即以當前節點為根的子樹的規模
     */
    int getSize();

    /**
     * @return 當前節點的高度
     */
    int getHeight();

    /**
     * @return 當前節點的深度
     */
    int getDepth();
}
複製程式碼

基於連結串列實現樹

public class TreeLinkedList implements Tree {

    /**
     * 樹根節點
     */
    private Object element;
    /**
     * 父親、長子及最大的弟弟
     */
    private TreeLinkedList parent, firstChild, nextSibling;


    /**
     * (單節點樹)構造方法
     */
    public TreeLinkedList() {
        this(null, null, null, null);
    }

    /**
     * 構造方法
     * @param e
     * @param p
     * @param c
     * @param s
     */
    public TreeLinkedList(Object e, TreeLinkedList p, TreeLinkedList c, TreeLinkedList s) {
        element = e;
        parent = p;
        firstChild = c;
        nextSibling = s;
    }

    /*---------- Tree介面中各方法的實現 ----------*/

    /**
     * 返回當前節點中存放的物件
     *
     * @return
     */
    @Override
    public Object getElem() {
        return element;
    }

    //

    /**
     * 將物件obj存入當前節點,並返回此前的內容
     *
     * @param obj
     * @return
     */
    @Override
    public Object setElem(Object obj) {
        Object bak = element;
        element = obj;
        return bak;
    }

    /**
     * 返回當前節點的父節點;對於根節點,返回null
     *
     * @return
     */
    @Override
    public TreeLinkedList getParent() {
        return parent;
    }

    /**
     * 返回當前節點的長子;若沒有孩子,則返回null
     *
     * @return
     */
    @Override
    public TreeLinkedList getFirstChild() {
        return firstChild;
    }

    /**
     * 返回當前節點的最大弟弟;若沒有弟弟,則返回null
     *
     * @return
     */
    @Override
    public TreeLinkedList getNextSibling() {
        return nextSibling;
    }


    /**
     * 返回當前節點後代元素的數目,即以當前節點為根的子樹的規模
     *
     * @return
     */
    @Override
    public int getSize() {
        /**
         * 當前節點也是自己的後代
         */
        int size = 1;
        /**
         * 從長子開始
         */
        TreeLinkedList subtree = firstChild;

        /**依次
         *
         */
        while (null != subtree) {
            /**
             * 累加
             */
            size += subtree.getSize();
            /**
             * 所有孩子的後代數目
             */
            subtree = subtree.getNextSibling();
        }
        /**
         * 即可得到當前節點的後代總數
         */
        return size;
    }

    /**
     * 返回當前節點的高度
     *
     * @return
     */
    @Override
    public int getHeight() {
        int height = -1;
        /**
         * 從長子開始
         */
        TreeLinkedList subtree = firstChild;
        /**
         * 依次
         */
        while (null != subtree) {
            /**
             * 在所有孩子中取最大高度
             */
            height = Math.max(height, subtree.getHeight());
            subtree = subtree.getNextSibling();
        }
        /**
         * 即可得到當前節點的高度
         */
        return height + 1;
    }

    /**
     * 返回當前節點的深度
     * @return
     */
    @Override
    public int getDepth() {
        int depth = 0;
        /**
         * 從父親開始
         */
        TreeLinkedList p = parent;
        /**
         * 依次
         */
        while (null != p) {
            depth++;
            /**訪問各個真祖先
             * 
             */
            p = p.getParent();
        }
        /**
         * 真祖先的數目,即為當前節點的深度
         */
        return depth;
    }
}

複製程式碼

前序、後序遍歷

所謂樹的遍歷(Traversal),就是按照某種次序訪問樹中的節點,且每個節點恰好訪問一次。 也就是說,按照被訪問的次序,可以得到由樹中所有節點排成的一個序列。 兩種最基本的樹遍歷演算法⎯⎯前序遍歷(PreorderTraversal)後序遍歷(Postorder traversal)。這兩種遍歷演算法都是遞迴定義的,只是其中對“次序”的定義略有 不同。

PreorderTraversal

        if (null != v) {
            for (u = v.getFirstChild(); null != u; u = u.getNextSibling())
                PreorderTraversal(u);
        }
複製程式碼

LevelorderTraversal

        if (null != v) {
            Q.enqueue(v);
            while (!Q.isEmpty()) {
                u = Q.dequeue();
                for (w = u.getFirstChild(); null != w; w = w.nextSibling())
                    Q.enqueue(w);
            }
        }
複製程式碼

樹迭代器

public class IteratorTree implements Iterator {
    /**
     * 列表
     */
    private List list;
    /**
     * 當前(下一個)元素的位置
     */
    private Position nextPosition;


    /**
     * 預設構造方法
     */
    public IteratorTree() {
        list = null;
    }

    /**
     * 前序遍歷
     *
     * @param T
     */
    public void elementsPreorderIterator(TreeLinkedList T) {
        if (null == T) {
            //遞迴基
            return;
        }
        //首先輸出當前節點
        list.insertLast(T);
        //從當前節點的長子開始
        TreeLinkedList subtree = T.getFirstChild();
        //依次對當前節點的各個孩子
        while (null != subtree) {
            //做前序遍歷
            this.elementsPreorderIterator(subtree);
            subtree = subtree.getNextSibling();
        }
    }

    /**
     * 後序遍歷
     *
     * @param T
     */
    public void elementsPostorderIterator(TreeLinkedList T) {
        if (null == T) {
            //遞迴基
            return;
        }
        //從當前節點的長子開始
        TreeLinkedList subtree = T.getFirstChild();
        //依次對當前節點的各個孩子
        while (null != subtree) {
            //做後序遍歷
            this.elementsPostorderIterator(subtree);
            subtree = subtree.getNextSibling();
        }
        //當所有後代都訪問過後,最後才訪問當前節點
        list.insertLast(T);
    }

    /**
     * 層次遍歷
     *
     * @param T
     */
    public void levelTraversalIterator(TreeLinkedList T) {
        if (null == T) {
            return;
        }
        //空隊
        QueueList Q = new QueueList();
        //根節點入隊
        Q.enqueue(T);
        //在佇列重新變空之前
        while (!Q.isEmpty()) {
            //取出佇列首節點
            TreeLinkedList tree = (TreeLinkedList) (Q.dequeue());
            //將新出隊的節點接入迭代器中
            list.insertLast(tree);
            //從tree的第一個孩子起
            TreeLinkedList subtree = tree.getFirstChild();
            //依次找出所有孩子,並
            while (null != subtree) {
                //將其加至佇列中
                Q.enqueue(subtree);
                subtree = subtree.getNextSibling();
            }
        }
    }

    /**
     * 檢查迭代器中是否還有剩餘的元素
     *
     * @return
     */
    @Override
    public boolean hasNext() {
        return (null != nextPosition);
    }

    /**
     * 返回迭代器中的下一元素
     *
     * @return
     * @throws ExceptionNoSuchElement
     */
    @Override
    public Object getNext() throws ExceptionNoSuchElement {
        if (!hasNext()) {
            throw new ExceptionNoSuchElement("No next position");
        }
        Position currentPosition = nextPosition;
        if (currentPosition == list.last()) {//若已到達尾元素,則
            //不再有下一元素
            nextPosition = null;
        } else {//轉向下一元素
            //否則
            nextPosition = list.getNext(currentPosition);
        }
        return currentPosition.getElem();
    }
} 

複製程式碼

二叉樹

二叉樹類的 Java 介面

/**
 * <b>Description:</b> 二叉樹介面 <br>
 */
public interface BinTree {

    /**
     * 返回樹根
     *
     * @return
     */
    public BinTreePosition getRoot();

    /**
     * 判斷是否樹空
     *
     * @return
     */
    public boolean isEmpty();

    /**
     * 返回樹的規模(即樹根的後代數目)
     *
     * @return
     */
    public int getSize();

    /**
     * 返回樹(根)的高度
     *
     * @return
     */
    public int getHeight();

    /**
     * 前序遍歷
     *
     * @return
     */
    public Iterator elementsPreorder();

    /**
     * 中序遍歷
     *
     * @return
     */
    public Iterator elementsInorder();

    /**
     * 後序遍歷
     *
     * @return
     */
    public Iterator elementsPostorder();

    /**
     * 層次遍歷
     *
     * @return
     */
    public Iterator elementsLevelorder();
}
複製程式碼

二叉樹節點ADT介面

為了在遵循物件導向規範的同時保證效率,這裡也將使用位置的概念來描述和實現二叉樹節點。

/**
 * <b>Description:</b> 二叉樹節點ADT介面 <br>
 */
public interface BinTreePosition extends Position {

    /**
     * 判斷是否有父親(為使程式碼描述簡潔)
     *
     * @return
     */
    public boolean hasParent();

    /**
     * 返回當前節點的父節點
     *
     * @return
     */
    public BinTreePosition getParent();

    /**
     * 設定當前節點的父節點
     *
     * @param p
     */
    public void setParent(BinTreePosition p);

    /**
     * 判斷是否為葉子
     *
     * @return
     */
    public boolean isLeaf();

    /**
     * 判斷是否為左孩子(為使程式碼描述簡潔)
     *
     * @return
     */
    public boolean isLChild();

    /**
     * 判斷是否有左孩子(為使程式碼描述簡潔)
     *
     * @return
     */
    public boolean hasLChild();

    /**
     * 返回當前節點的左孩子
     *
     * @return
     */
    public BinTreePosition getLChild();

    /**
     * 設定當前節點的左孩子(注意:this.lChild和c.parent都不一定為空)
     *
     * @param c
     */
    public void setLChild(BinTreePosition c);

    /**
     * 判斷是否為右孩子(為使程式碼描述簡潔)
     *
     * @return
     */
    public boolean isRChild();

    /**
     * 判斷是否有右孩子(為使程式碼描述簡潔)
     *
     * @return
     */
    public boolean hasRChild();

    /**
     * 返回當前節點的右孩子
     *
     * @return
     */
    public BinTreePosition getRChild();

    /**
     * 設定當前節點的右孩子(注意:this.rChild和c.parent都不一定為空)
     *
     * @param c
     */
    public void setRChild(BinTreePosition c);

    /**
     * 返回當前節點後代元素的數目
     *
     * @return
     */
    public int getSize();

    /**
     * 在孩子發生變化後,更新當前節點及其祖先的規模
     */
    public void updateSize();

    /**
     * 返回當前節點的高度
     *
     * @return
     */
    public int getHeight();

    /**
     * 在孩子發生變化後,更新當前節點及其祖先的高度
     */
    public void updateHeight();

    /**
     * 返回當前節點的深度
     *
     * @return
     */
    public int getDepth();

    /**
     * 在父親發生變化後,更新當前節點及其後代的深度
     */
    public void updateDepth();

    /**
     * 按照中序遍歷的次序,找到當前節點的直接前驅
     *
     * @return
     */
    public BinTreePosition getPrev();

    /**
     * 按照中序遍歷的次序,找到當前節點的直接後繼
     *
     * @return
     */
    public BinTreePosition getSucc();

    /**
     * 斷絕當前節點與其父親的父子關係 返回當前節點
     *
     * @return
     */
    public BinTreePosition secede();

    /**
     * 將節點c作為當前節點的左孩子
     *
     * @param c
     * @return
     */
    public BinTreePosition attachL(BinTreePosition c);

    /**
     * 將節點c作為當前節點的右孩子
     *
     * @param c
     * @return
     */
    public BinTreePosition attachR(BinTreePosition c);

    /**
     * 前序遍歷
     *
     * @return
     */
    public Iterator elementsPreorder();

    /**
     * 中序遍歷
     *
     * @return
     */
    public Iterator elementsInorder();

    /**
     * 後序遍歷
     *
     * @return
     */
    public Iterator elementsPostorder();

    /**
     * 層次遍歷
     *
     * @return
     */
    public Iterator elementsLevelorder();
}
複製程式碼

基於連結串列節點實現二叉樹節點

/**
 * <b>Description:</b> 基於連結串列節點實現二叉樹節點 <br>
 */
public class BinTreeNode implements BinTreePosition {
    /**
     * 該節點中存放的物件
     */
    protected Object element;
    /**
     * 父親
     */
    protected BinTreePosition parent;
    /**
     * 左孩子
     */
    protected BinTreePosition lChild;
    /**
     * 右孩子
     */
    protected BinTreePosition rChild;
    /**
     * 後代數目
     */
    protected int size;
    /**
     * 高度
     */
    protected int height;
    /**
     * 深度
     */
    protected int depth;


    /**************************** 構造方法 ****************************/
    public BinTreeNode() {
        this(null, null, true, null, null);
    }

    /**
     * @param e        節點內容
     * @param p        父節點
     * @param asLChild 是否作為父節點的左孩子
     * @param l        左孩子
     * @param r        右孩子
     */
    public BinTreeNode(Object e, BinTreePosition p, boolean asLChild, BinTreePosition l, BinTreePosition r) {
        size = 1;
        height = depth = 0;
        //初始化
        parent = lChild = rChild = null;
        //存放的物件 
        element = e;
        // 建立與父親的關係
        if (null != p) {
            if (asLChild) {
                p.attachL(this);
            } else {
                p.attachR(this);
            }
        }
        //建立與孩子的關係
        if (null != l) {
            attachL(l);
        }
        if (null != r) {
            attachR(r);
        }
    }

    /**************************** Position介面方法 ********************************/
    /**
     * 返回當前節點中存放的物件
     *
     * @return
     */
    @Override
    public Object getElem() {
        return element;
    }

    /**
     * 將物件obj存入當前節點,並返回此前的內容
     *
     * @param obj
     * @return
     */
    @Override
    public Object setElem(Object obj) {
        Object bak = element;
        element = obj;
        return bak;
    }

    /**************************** BinTreePosition介面方法 *************************/
    /**
     * 判斷是否有父親(為使程式碼描述簡潔)
     *
     * @return
     */
    @Override
    public boolean hasParent() {
        //返回當前節點的父節點
        return null != parent;
    }

    @Override
    public BinTreePosition getParent() {
        //設定當前節點的父節點
        return parent;
    }

    @Override
    public void setParent(BinTreePosition p) {
        parent = p;
    }

    /**
     * 判斷是否為葉子
     *
     * @return
     */
    @Override
    public boolean isLeaf() {
        //判斷是否為左孩子(為使程式碼描述簡潔)
        return !hasLChild() && !hasRChild();
    }

    /**
     * 若當前節點有父親,而且是左孩子,則返回true;否則,返回false
     *
     * @return
     */
    @Override
    public boolean isLChild() {
        return (hasParent() && this == getParent().getLChild()) ? true : false;
    }

    /**
     * 判斷是否有左孩子(為使程式碼描述簡潔)
     *
     * @return
     */
    @Override
    public boolean hasLChild() {
        return null != lChild;
    }

    /**
     * 返回當前節點的左孩子
     *
     * @return
     */
    @Override
    public BinTreePosition getLChild() {
        return lChild;
    }

    /**
     * 設定當前節點的左孩子(注意:this.lChild和c.parent都不一定為空)
     *
     * @param c
     */
    @Override
    public void setLChild(BinTreePosition c) {
        lChild = c;
    }

    /**
     * 判斷是否為右孩子(為使程式碼描述簡潔)
     *
     * @return 若當前節點有父親,而且是右孩子,則返回true;否則,返回false
     */
    @Override
    public boolean isRChild() {
        return (hasParent() && this == getParent().getRChild()) ? true : false;
    }

    /**
     * 判斷是否有右孩子(為使程式碼描述簡潔)
     *
     * @return
     */
    @Override
    public boolean hasRChild() {
        return null != rChild;
    }

    /**
     * 返回當前節點的右孩子
     *
     * @return
     */
    @Override
    public BinTreePosition getRChild() {
        return rChild;
    }

    /**
     * 設定當前節點的右孩子(注意:this.rChild和c.parent都不一定為空)
     *
     * @param c
     */
    @Override
    public void setRChild(BinTreePosition c) {
        rChild = c;
    }

    /**
     * 返回當前節點後代元素的數目
     *
     * @return
     */
    @Override
    public int getSize() {
        return size;
    }

    /**
     * 在孩子發生變化後,更新當前節點及其祖先的規模
     */
    @Override
    public void updateSize() {
        //當前節點
        size = 1;
        if (hasLChild()) {
            //左子樹的規模
            size += getLChild().getSize();
        }
        if (hasRChild()) {
            //右子樹的規模
            size += getRChild().getSize();
        }

        if (hasParent()) {
            //遞迴更新各個真祖先的規模記錄
            getParent().updateSize();
        }
    }

    /**
     * 返回當前節點的高度
     *
     * @return
     */
    @Override
    public int getHeight() {
        return height;
    }

    /**
     * 在孩子發生變化後,更新當前節點及其祖先的高度
     */
    @Override
    public void updateHeight() {
        //先假設沒有左、右孩子
        height = 0;

        if (hasLChild()) {
            //左孩子
            height = Math.max(height, 1 + getLChild().getHeight());
        }
        if (hasRChild()) {
            //右孩子
            height = Math.max(height, 1 + getRChild().getHeight());
        }
        if (hasParent()) {
            //遞迴更新各個真祖先的高度記錄
            getParent().updateHeight();
        }
    }

    /**
     * 返回當前節點的深度
     *
     * @return
     */
    @Override
    public int getDepth() {
        return depth;
    }

    /**
     * 在父親發生變化後,更新當前節點及其後代的深度
     */
    @Override
    public void updateDepth() {
        //當前節點
        depth = hasParent() ? 1 + getParent().getDepth() : 0;

        if (hasLChild()) {
            //沿孩子引用逐層向下,
            getLChild().updateDepth();
        }
        if (hasRChild()) {
            //遞迴地更新所有後代的深度記錄
            getRChild().updateDepth();
        }
    }

    /**
     * 按照中序遍歷的次序,找到當前節點的直接前驅
     *
     * @return
     */
    @Override
    public BinTreePosition getPrev() {
        //若左子樹非空,則其中的最大者即為當前節點的直接前驅
        if (hasLChild()) {
            return findMaxDescendant(getLChild());
        }
        //至此,當前節點沒有左孩子
        if (isRChild()) {
            //若當前節點是右孩子,則父親即為其直接前驅
            return getParent();
        }
        //至此,當前節點沒有左孩子,而且是左孩子
        //從當前節點出發
        BinTreePosition v = this;
        while (v.isLChild()) {
            //沿左孩子鏈一直上升
            v = v.getParent();
        }
        //至此,v或者沒有父親,或者是父親的右孩子
        return v.getParent();
    }

    /**
     * 按照中序遍歷的次序,找到當前節點的直接後繼
     *
     * @return
     */
    @Override
    public BinTreePosition getSucc() {
        //若右子樹非空,則其中的最小者即為當前節點的直接後繼
        if (hasRChild()) {
            return findMinDescendant(getRChild());
        }
        //至此,當前節點沒有右孩子
        if (isLChild()) {
            //若當前節點是左孩子,則父親即為其直接後繼
            return getParent();
        }
        //至此,當前節點沒有右孩子,而且是右孩子
        //從當前節點出發
        BinTreePosition v = this;
        while (v.isRChild()) {
            //沿右孩子鏈一直上升
            v = v.getParent();
        }
        //至此,v或者沒有父親,或者是父親的左孩子
        return v.getParent();
    }

    /**
     * 斷絕當前節點與其父親的父子關係。
     * 將以某一節點為根的子樹從母樹中分離出來。
     *
     * @return 返回當前節點
     */
    @Override
    public BinTreePosition secede() {
        if (null != parent) {
            if (isLChild()) {
                //切斷父親指向當前節點的引用
                parent.setLChild(null);
            } else {
                parent.setRChild(null);
            }
            //更新當前節點及其祖先的規模
            parent.updateSize();
            //更新當前節點及其祖先的高度
            parent.updateHeight();
            //切斷當前節點指向原父親的引用
            parent = null;
            //更新節點及其後代節點的深度
            updateDepth();
        }
        //返回當前節點
        return this;
    }

    /**
     * 將節點c作為當前節點的左孩子
     *
     * @param c
     * @return
     */
    @Override
    public BinTreePosition attachL(BinTreePosition c) {
        if (hasLChild()) {
            //摘除當前節點原先的左孩子
            getLChild().secede();
        }
        if (null != c) {
            //c脫離原父親
            c.secede();
            lChild = c;
            //確立新的父子關係
            c.setParent(this);
            //更新當前節點及其祖先的規模
            updateSize();
            //更新當前節點及其祖先的高度
            updateHeight();
            //更新c及其後代節點的深度
            c.updateDepth();
        }
        return this;
    }

    /**
     * 將節點c作為當前節點的右孩子
     *
     * @param c
     * @return
     */
    @Override
    public BinTreePosition attachR(BinTreePosition c) {
        if (hasRChild()) {
            //摘除當前節點原先的右孩子
            getRChild().secede();
        }
        if (null != c) {
            //c脫離原父親
            c.secede();
            rChild = c;
            //確立新的父子關係
            c.setParent(this);
            //更新當前節點及其祖先的規模
            updateSize();
            //更新當前節點及其祖先的高度
            updateHeight();
            //更新c及其後代節點的深度
            c.updateDepth();
        }
        return this;
    }

    /**
     * 前序遍歷
     *
     * @return
     */
    @Override
    public Iterator elementsPreorder() {
        List list = new ListDLNode();
        preorder(list, this);
        return list.elements();
    }

    /**
     * 中序遍歷
     *
     * @return
     */
    @Override
    public Iterator elementsInorder() {
        List list = new ListDLNode();
        inorder(list, this);
        return list.elements();
    }

    /**
     * 後序遍歷
     *
     * @return
     */
    @Override
    public Iterator elementsPostorder() {
        List list = new ListDLNode();
        postorder(list, this);
        return list.elements();
    }

    /**
     * 層次遍歷
     *
     * @return
     */
    @Override
    public Iterator elementsLevelorder() {
        List list = new ListDLNode();
        levelorder(list, this);
        return list.elements();
    }

    /**************************** 輔助方法 ****************************/
    /**
     * 在v的後代中,找出最小者
     *
     * @param v
     * @return
     */
    protected static BinTreePosition findMinDescendant(BinTreePosition v) {
        if (null != v) {
            while (v.hasLChild()) {
                //從v出發,沿左孩子鏈一直下降
                v = v.getLChild();
            }
        }
        //至此,v或者為空,或者沒有左孩子
        return v;
    }

    /**
     * 在v的後代中,找出最大者
     *
     * @param v
     * @return
     */
    protected static BinTreePosition findMaxDescendant(BinTreePosition v) {
        if (null != v) {
            while (v.hasRChild()) {
                //從v出發,沿右孩子鏈一直下降
                v = v.getRChild();
            }
        }
        //至此,v或者為空,或者沒有右孩子
        return v;
    }

    /**
     * 前序遍歷以v為根節的(子)樹
     *
     * @param list
     * @param v
     */
    protected static void preorder(List list, BinTreePosition v) {

        if (null == v) {
            //遞迴基:空樹
            return;
        }
        //訪問v
        list.insertLast(v);
        //遍歷左子樹
        preorder(list, v.getLChild());
        //遍歷右子樹
        preorder(list, v.getRChild());
    }

    /**
     * 中序遍歷以v為根節的(子)樹
     *
     * @param list
     * @param v
     */
    protected static void inorder(List list, BinTreePosition v) {
        if (null == v) {
            //遞迴基:空樹
            return;
        }
        //遍歷左子樹
        inorder(list, v.getLChild());
        //訪問v
        list.insertLast(v);
        //遍歷右子樹
        inorder(list, v.getRChild());
    }

    /**
     * 後序遍歷以v為根節的(子)樹
     *
     * @param list
     * @param v
     */
    protected static void postorder(List list, BinTreePosition v) {
        if (null == v) {
            //遞迴基:空樹
            return;
        }
        //遍歷左子樹
        postorder(list, v.getLChild());
        //遍歷右子樹
        postorder(list, v.getRChild());
        //訪問v
        list.insertLast(v);
    }

    /**
     * 層次遍歷以v為根節的(子)樹
     *
     * @param list
     * @param v
     */
    protected static void levelorder(List list, BinTreePosition v) {
        //空隊
        QueueList Q = new QueueList();
        //根節點入隊
        Q.enqueue(v);
        while (!Q.isEmpty()) {
            //出隊
            BinTreePosition u = (BinTreePosition) Q.dequeue();
            //訪問v
            list.insertLast(u);
            if (u.hasLChild()) {
                Q.enqueue(u.getLChild());
            }
            if (u.hasRChild()) {
                Q.enqueue(u.getRChild());
            }
        }
    }
}

複製程式碼

基於連結串列實現二叉樹

/**
 * <b>Create Date:</b> 2018/9/25<br>
 * <b>Email:</b> 289286298@qq.com<br>
 * <b>Description:</b> 基於連結串列實現二叉樹 <br>
 *
 * @author tongson
 */
public class BinTreeLinkedList implements BinTree {
    /**
     * 根節點
     */
    protected BinTreePosition root;

    /**************************** 建構函式 ****************************/
    public BinTreeLinkedList() {
        this(null);
    }

    public BinTreeLinkedList(BinTreePosition r) {
        root = r;
    }

    /**************************** BinaryTree介面方法 ****************************/
    /**
     * 返回樹根
     *
     * @return
     */
    @Override
    public BinTreePosition getRoot() {
        return root;
    }

    /**
     * 判斷是否樹空
     *
     * @return
     */
    @Override
    public boolean isEmpty() {
        return null == root;
    }

    /**
     * 返回樹的規模(即樹根的後代數目)
     *
     * @return
     */
    @Override
    public int getSize() {
        return isEmpty() ? 0 : root.getSize();
    }

    /**
     * 返回樹(根)的高度
     *
     * @return
     */
    @Override
    public int getHeight() {
        return isEmpty() ? -1 : root.getHeight();
    }

    /**
     * 前序遍歷
     *
     * @return
     */
    @Override
    public Iterator elementsPreorder() {
        return root.elementsPreorder();
    }

    /**
     * 中序遍歷
     *
     * @return
     */
    @Override
    public Iterator elementsInorder() {
        return root.elementsInorder();
    }

    /**
     * 後序遍歷
     *
     * @return
     */
    @Override
    public Iterator elementsPostorder() {
        return root.elementsPostorder();
    }

    /**
     * 層次遍歷
     *
     * @return
     */
    @Override
    public Iterator elementsLevelorder() {
        return root.elementsLevelorder();
    }
} 
複製程式碼

完全二叉樹

介面

/**
 * <b>Description:</b> 完全二叉樹介面 <br>
 */
public interface ComplBinTree extends BinTree {
    /**
     * 生成並返回一個存放e的外部節點,該節點成為新的末節點
     *
     * @param e
     * @return
     */
    BinTreePosition addLast(Object e);

    /**
     * 刪除末節點,並返回其中存放的內容
     *
     * @return
     */
    Object delLast();

    /**
     * 返回按照層次遍歷編號為i的節點的位置,0 <= i < size()
     *
     * @param i
     * @return
     */
    BinTreePosition posOfNode(int i);
}
複製程式碼

基於秩實現的完全二叉樹節點

/**
 * <b>Description:</b> 基於秩實現的完全二叉樹節點 <br>
 */
public class ComplBinTreeNodeRank extends BinTreeNode implements BinTreePosition {
    /**
     * 所屬的樹
     */
    private Vector T;
    /**
     * 在所屬樹中的秩
     */
    private int rank;
    /**
     * 存放的物件
     */
    private Object element;

    /**
     * 建構函式
     *
     * @param t
     * @param obj
     */
    public ComplBinTreeNodeRank(Vector t, Object obj) {
        element = obj;
        T = t;
        rank = T.getSize();
        T.insertAtRank(rank, this);
    }

    /**
     * 返回當前節點中存放的物件
     *
     * @return
     */
    @Override
    public Object getElem() {
        return element;
    }

    /**
     * 將物件obj存入當前節點,並返回此前的內容
     *
     * @param obj
     * @return
     */
    @Override
    public Object setElem(Object obj) {
        Object bak = element;
        element = obj;
        return bak;
    }

    /**
     * 判斷是否有父親(為使程式碼描述簡潔)
     *
     * @return
     */
    @Override
    public boolean hasParent() {
        return (0 != rank) ? true : false;
    }

    /**
     * 返回當前節點的父節點
     *
     * @return
     */
    @Override
    public BinTreePosition getParent() {
        return hasParent() ? (BinTreePosition) T.getAtRank((rank - 1) / 2) : null;
    }

    /**
     * 判斷是否有左孩子(為使程式碼描述簡潔)
     *
     * @return
     */
    @Override
    public boolean hasLChild() {
        return (1 + rank * 2 < T.getSize()) ? true : false;
    }

    /**
     * 返回當前節點的左孩子
     *
     * @return
     */
    @Override
    public BinTreePosition getLChild() {
        return hasLChild() ? (BinTreePosition) (T.getAtRank(1 + rank * 2)) : null;
    }

    /**
     * 判斷是否有右孩子(為使程式碼描述簡潔)
     *
     * @return
     */
    @Override
    public boolean hasRChild() {
        return (2 + rank * 2 < T.getSize()) ? true : false;
    }

    /**
     * 返回當前節點的右孩子
     *
     * @return
     */
    @Override
    public BinTreePosition getRChild() {
        return hasRChild() ? (BinTreePosition) (T.getAtRank(2 + rank * 2)) : null;
    }

    /**
     * 返回當前節點後代元素的數目
     *
     * @return
     */
    @Override
    public int getSize() {
        int size = 1;
        if (hasLChild()) {
            size += getLChild().getSize();
        }
        if (hasRChild()) {
            size += getRChild().getSize();
        }
        return size;
    }

    /**
     * 返回當前節點的高度
     *
     * @return
     */
    @Override
    public int getHeight() {
        int hL = hasLChild() ? getLChild().getHeight() : -1;
        int hR = hasRChild() ? getRChild().getHeight() : -1;
        return 1 + Math.max(hL, hR);
    }

    /**
     * 返回當前節點的深度
     *
     * @return
     */
    @Override
    public int getDepth() {
        return hasParent() ? 1 + getParent().getDepth() : 0;
    }
}
複製程式碼

基於向量的實現

/**
 * <b>Description:</b>  基於向量實現的完全二叉樹 <br>
 */
public class ComplBinTreeVector extends BinTreeLinkedList implements ComplBinTree {
    /**
     * 向量
     */
    private Vector T;

    /**
     * 構造方法:預設的空樹
     */
    public ComplBinTreeVector() {
        T = new VectorExtArray();
        root = null;
    }

    /**
     * 構造方法:按照給定的節點序列,批量式建立完全二叉樹
     *
     * @param s
     */
    public ComplBinTreeVector(Sequence s) {
        this();
        if (null != s) {
            while (!s.isEmpty()) {
                addLast(s.removeFirst());
            }
        }
    }

    /*---------- BinaryTree介面中各方法的實現 ----------*/

    /**
     * 返回樹根(重寫)
     *
     * @return
     */
    @Override
    public BinTreePosition getRoot() {
        return T.isEmpty() ? null : posOfNode(0);
    }

    /**
     * 判斷是否樹空(重寫)
     *
     * @return
     */
    @Override
    public boolean isEmpty() {
        return T.isEmpty();
    }

    /**
     * 返回樹的規模(重寫)
     *
     * @return
     */
    @Override
    public int getSize() {
        return T.getSize();
    }

    /**
     * 返回樹(根)的高度(重寫)
     *
     * @return
     */
    @Override
    public int getHeight() {
        return isEmpty() ? -1 : getRoot().getHeight();
    }

    /*---------- ComplBinTree介面中各方法的實現 ----------*/

    /**
     * 生成並返回一個存放e的外部節點,該節點成為新的末節點
     *
     * @param e
     * @return
     */
    @Override
    public BinTreePosition addLast(Object e) {
        BinTreePosition node = new ComplBinTreeNodeRank(T, e);
        root = (BinTreePosition) T.getAtRank(0);
        return node;
    }

    /**
     * 刪除末節點,並返回其中存放的內容
     *
     * @return
     */
    @Override
    public Object delLast() {
        if (isEmpty()) {
            //若樹(堆)已空,無法刪除
            return null;
        }
        if (1 == getSize()) {
            //若刪除最後一個節點,則樹空
            root = null;
        }
        return T.removeAtRank(T.getSize() - 1);
    }

    /**
     * 返回按照層次遍歷編號為i的節點的位置,0 <= i < size()
     *
     * @param i
     * @return
     */
    @Override
    public BinTreePosition posOfNode(int i) {
        return (BinTreePosition) T.getAtRank(i);
    }
}
複製程式碼

相關文章