數據結構(十) -- 二叉樹

一,二叉樹

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

在算法領域,二叉樹的重要地位是其它結構無法替代的。所謂二叉樹就是各節點不超過2 度的有序樹,因此每個節點的孩子(如果存在的話)可以左、右區分,分別稱作左孩子和右孩子——之所以如此命名,是因為在畫出二叉樹時,通常都將左、右孩子分別畫在其父節點的左下方、右下方。


二,二叉樹ADT

操作方法 功能描述
getElement() 返回存放當前節點處的對象
輸入:無
輸出:對象
setElement(e) 將對象e 存入當前節點,并返回其中此前所存的內容
輸入:一個對象
輸出:對象
getParent() 返回當前節點的父節點
輸入:無
輸出:樹節點
getLChild() 返回當前節點的左孩子
輸入:無
輸出:二叉樹節點
getRChild() 返回當前節點的右孩子
輸入:無
輸出:二叉樹節點

三,基于鏈表實現二叉樹

二叉樹的基本算法分析
  • ** getSize()、getHeight()和getDepth() **
    這三個方法的功能是,分別返回二叉子樹的規模、樹根節點的高度和深度。
    這里為每個節點設置了三個變量size、height和depth,分別對應于這三個指標,這樣,只需在O(1)時間內返回相應的變量,即可實現相應的功能。

  • **updateSize() **
    若當前節點的孩子發生變化,比如原有的某個孩子被刪除或者有新的孩子插入,就需要更新當前節點及其祖先的規模記錄,以便后續的查詢。

算法:updateSize(v)
輸入:二叉樹中任一節點v
輸出:更新v的后代規模記錄
{
令size(v) = 1 + size(lc) + size(rc);
若v的父親p存在,則調用updateSize(p),遞歸地更新父親的規模記錄;//尾遞歸,可改寫為迭代形式
}

  • updateHeight()
    同樣地,在孩子發生變化后,也有必要更新當前節點的高度記錄。

算法:updateHeight(v)
輸入:二叉樹中任一節點v
輸出:更新v的高度記錄
{
height(v) = 0;//先假設沒有左、右孩子
若v有左孩子lc,則令:height(v) = Max(height(v), 1 + height(lc));
若v有右孩子lc,則令:height(v) = Max(height(v), 1 + height(rc));
若v的父親p存在,則調用updateHeight(p),遞歸地更新父親的高度記錄;
}

  • updateDepth()
    在父親節點發生變化后,有必要更新當前節點的深度記錄。

算法:updateDepth(v)
輸入:二叉樹中任一節點v
輸出:更新v的深度記錄
{
若v的父親節點p存在,則令depth(v) = depth(p)+1;
否則,令depth(v) = 0;
若v的左孩子lc存在,則調用updateDepth(lc);//沿孩子引用逐層向下,
若v的右孩子rc存在,則調用updateDepth(rc);//遞歸地更新所有后代的深度記錄
}

  • secede()
    為了簡化二叉樹的動態操作的實現,這里專門設計了一個secede()方法。,該
    方法的功能是,將以某一節點為根的子樹從母樹中分離出來。

算法:secede(v)
輸入:二叉樹中任一節點v
輸出:將以v為根的子樹叢母樹中分離出來
{
若v有父親{
切斷父親指向v的引用;
調用updateSize(v)和updateHeight(v),更新v及其祖先的規模記錄和高度記錄;
切斷v指向父親的引用;
調用updateDepth(v),更新v及其后代的深度記錄;
}

}

  • attachL()和attachR()
    這一對方法的功能是,將節點c作為當前節點的左或右孩子與節點v聯接起來。

算法:attachL(p, c)
輸入:兩個二叉樹節點p與c
輸出:將c作為左孩子,與p聯接起來
{
若p已經有左孩子lc,則首先調用secede(lc)將其摘除;
調用secede(c),使c及其后代脫離原屬母樹;

設置相應的引用,在p和c之間建立父子關系;
調用updateSize(p)和updateHeight(p),更新節點p及其祖先的規模和高度;
調用updateDepth(c),更新c及其后代節點的深度;
}

二叉樹節點接口:BinTreePosition
package dsa.BinTree;

/*
* 二叉樹節點ADT接口
*/

import dsa.Iterator.Iterator;

/*
* 二叉樹節點ADT接口
*/

import other.Position;

/*
* 二叉樹節點ADT接口
*/

public interface BinTreePosition extends Position {
    // 判斷是否有父親(為使代碼描述簡潔)
    public boolean hasParent();

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

    // 設置當前節點的父節點
    public void setParent(BinTreePosition p);

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

    // 判斷是否為左孩子(為使代碼描述簡潔)
    public boolean isLChild();

    // 判斷是否有左孩子(為使代碼描述簡潔)
    public boolean hasLChild();

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

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

    // 判斷是否為右孩子(為使代碼描述簡潔)
    public boolean isRChild();

    // 判斷是否有右孩子(為使代碼描述簡潔)
    public boolean hasRChild();

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

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

    // 返回當前節點后代元素的數目
    public int getSize();

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

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

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

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

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

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

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

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

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

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

    // 前序遍歷
    public Iterator elementsPreorder();

    // 中序遍歷
    public Iterator elementsInorder();

    // 后序遍歷
    public Iterator elementsPostorder();

    // 層次遍歷
    public Iterator elementsLevelorder();
}
基于鏈表節點實現二叉樹節點:BinTreeNode
package dsa.BinTree;

/*
* 基于鏈表節點實現二叉樹節點
*/

import dsa.Iterator.Iterator;
import dsa.List.List;
import dsa.List.List_DLNode;
import dsa.Queue.Queue_List;

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);
    }

    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接口方法 ********************************/
    // 返回當前節點中存放的對象
    public Object getElem() {
        return element;
    }

    // 將對象obj存入當前節點,并返回此前的內容
    public Object setElem(Object obj) {
        Object bak = element;
        element = obj;
        return bak;
    }

    /**************************** BinTreePosition接口方法 *************************/
    // 判斷是否有父親(為使代碼描述簡潔)
    public boolean hasParent() {
        return null != parent;
    }

    // 返回當前節點的父節點
    public BinTreePosition getParent() {
        return parent;
    }

    // 設置當前節點的父節點
    public void setParent(BinTreePosition p) {
        parent = p;
    }

    // 判斷是否為葉子
    public boolean isLeaf() {
        return !hasLChild() && !hasRChild();
    }

    // 判斷是否為左孩子(為使代碼描述簡潔)
    // 若當前節點有父親,而且是左孩子,則返回true;否則,返回false
    public boolean isLChild() {
        return (hasParent() && this == getParent().getLChild()) ? true : false;
    }

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

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

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

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

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

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

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

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

    // 在孩子發生變化后,更新當前節點及其祖先的規模
    public void updateSize() {
        size = 1;// 當前節點
        if (hasLChild())
            size += getLChild().getSize();// 左子樹的規模
        if (hasRChild())
            size += getRChild().getSize();// 右子樹的規模
        if (hasParent())
            getParent().updateSize();// 遞歸更新各個真祖先的規模記錄
    }

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

    // 在孩子發生變化后,更新當前節點及其祖先的高度
    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();// 遞歸更新各個真祖先的高度記錄
    }

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

    // 在父親發生變化后,更新當前節點及其后代的深度
    public void updateDepth() {
        depth = hasParent() ? 1 + getParent().getDepth() : 0;// 當前節點
        if (hasLChild())
            getLChild().updateDepth();// 沿孩子引用逐層向下,
        if (hasRChild())
            getRChild().updateDepth();// 遞歸地更新所有后代的深度記錄
    }

    // 按照中序遍歷的次序,找到當前節點的直接前驅
    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();
    }

    // 按照中序遍歷的次序,找到當前節點的直接后繼
    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();
    }

    // 斷絕當前節點與其父親的父子關系
    // 返回當前節點
    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作為當前節點的左孩子
    public BinTreePosition attachL(BinTreePosition c) {
        if (hasLChild())
            getLChild().secede();// 摘除當前節點原先的左孩子
        if (null != c) {
            c.secede();// c脫離原父親
            lChild = c;
            c.setParent(this);// 確立新的父子關系
            updateSize();// 更新當前節點及其祖先的規模
            updateHeight();// 更新當前節點及其祖先的高度
            c.updateDepth();// 更新c及其后代節點的深度
        }
        return this;
    }

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

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

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

    // 后序遍歷
    public Iterator elementsPostorder() {
        List list = new List_DLNode();
        postorder(list, this);
        return list.elements();
    }

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

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

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

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

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

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

    // 層次遍歷以v為根節的(子)樹
    protected static void levelorder(List list, BinTreePosition v) {
        Queue_List Q = new Queue_List();// 空隊
        Q.enqueue(v);// 根節點入隊
        while (!Q.isEmpty()) {
            BinTreePosition u = (BinTreePosition) Q.dequeue();// 出隊
            list.insertLast(u);// 訪問v
            if (u.hasLChild())
                Q.enqueue(u.getLChild());
            if (u.hasRChild())
                Q.enqueue(u.getRChild());
        }
    }
}
二叉樹接口:BinTree
package dsa.BinTree;

import dsa.Iterator.Iterator;

/*
* 二叉樹接口
*/
public interface BinTree {
    // 返回樹根
    public BinTreePosition getRoot();

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

    // 返回樹的規模(即樹根的后代數目)
    public int getSize();

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

    // 前序遍歷
    public Iterator elementsPreorder();

    // 中序遍歷
    public Iterator elementsInorder();

    // 后序遍歷
    public Iterator elementsPostorder();

    // 層次遍歷
    public Iterator elementsLevelorder();
}
基于鏈表實現二叉樹:BinTree_LinkedList
package dsa.BinTree;

import dsa.Iterator.Iterator;

/*
* 基于鏈表實現二叉樹
*/
public class BinTree_LinkedList implements BinTree {
    protected BinTreePosition root;// 根節點

    /**************************** 構造函數 ****************************/
    public BinTree_LinkedList() {
        this(null);
    }

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

    /**************************** BinaryTree接口方法 ****************************/
    // 返回樹根
    public BinTreePosition getRoot() {
        return root;
    }

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

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

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

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

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

    // 后序遍歷
    public Iterator elementsPostorder() {
        return root.elementsPostorder();
    }

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

推薦閱讀更多精彩內容