二叉樹的遍歷和重建

二叉樹是樹的一種,由一個根節(jié)點和兩棵互不相交左右子樹的二叉樹組成会烙。
二叉樹有很多性質(zhì)刑巧,就不一一舉例了~
先來說一下最簡單的關(guān)于二叉樹常用的3種遍歷方式田炭。層序遍歷就略過了~

前序遍歷:根節(jié)點未妹,左節(jié)點娩贷,右節(jié)點横腿。

結(jié)果就是:ABDGHCEIF


前序遍歷
中序遍歷:左節(jié)點开缎,根節(jié)點,右節(jié)點仗岸。

結(jié)果就是:GDHBAEICF


中序遍歷
后序遍歷:左節(jié)點糊闽,右節(jié)點,根節(jié)點爹梁。

結(jié)果就是:GHDBIEFCA


后序遍歷

概念就說到這里右犹,開始敲代碼~
首先,建立數(shù)節(jié)點姚垃。

public class TreeNode {

    private int index;      
 
    private String data;           //數(shù)據(jù)域 

    private TreeNode leftChild;    //左子樹

    private TreeNode rightChild;   //右子樹

    public int getIndex() {
        return index;
    }


    public void setIndex(int index) {
        this.index = index;
    }


    public String getData() {
        return data;
    }


    public void setData(String data) {
        this.data = data;
    }

     public TreeNodeString(String data) {
        this.data = data;
    }

    public TreeNode(int index, String data) {
        this.index = index;
        this.data = data;
        this.leftChild = null;
        this.rightChild = null;
    }
}

然后我們來構(gòu)建一個二叉樹:

/**
 * 構(gòu)建二叉樹
 *                  A
 *              B       C
 *          D       E      F
 *      G       H     I
 */
public void createBinaryTree() {
    //我們需要一個根節(jié)點
    root = new TreeNode(1, "A");   
    TreeNode treeNodeB = new TreeNode(2, "B");
    TreeNode treeNodeC = new TreeNode(3, "C");
    TreeNode treeNodeD = new TreeNode(4, "D");
    TreeNode treeNodeE = new TreeNode(5, "E");
    TreeNode treeNodeF = new TreeNode(6, "F");
    TreeNode treeNodeG = new TreeNode(7, "G");
    TreeNode treeNodeH = new TreeNode(8, "H");
    TreeNode treeNodeI = new TreeNode(9, "I");
    //分別對應(yīng)節(jié)點的左右子樹念链。
    root.leftChild = treeNodeB;
    root.rightChild = treeNodeC;
    treeNodeB.leftChild = treeNodeD;
    treeNodeD.leftChild = treeNodeG;
    treeNodeD.rightChild = treeNodeH;
    treeNodeC.leftChild = treeNodeE;
    treeNodeC.rightChild = treeNodeF;
    treeNodeE.rightChild = treeNodeI;

}

二叉樹建立完成之后,
開始遍歷积糯,首先使用循環(huán)的方式進行前序遍歷掂墓,循環(huán)需要借助棧來實現(xiàn)遍歷。

/**
 *  前序遍歷  非遞歸
 * @param treeNode
 */
public void nonRecOrder(TreeNode treeNode){
    if(treeNode == null){
        return;
    }
    Stack<TreeNode> stack = new Stack<>();
    //首先把根節(jié)點放入棧中
    stack.push(treeNode);
    while (!stack.isEmpty()){
        //彈出根結(jié)點
        TreeNode node = stack.pop();
        System.out.println("data:" + node.data);
        //如果有右子結(jié)點看成,則壓入節(jié)點
        if(node.rightChild!=null){
            //如果左節(jié)點有值君编,則右節(jié)點在棧底,最后才會輸出
            stack.push(node.rightChild);
        }
        //如果有右左結(jié)點川慌,則壓入節(jié)點
        if(node.leftChild!=null){
            //下次循環(huán)把左子節(jié)點當根節(jié)點繼續(xù)彈出
            stack.push(node.leftChild);
        }
    }
}

接著來介紹使用遞歸的方式進行遍歷

/**
 *  前序遍歷  遞歸
 * @param treeNode
 */
public void preOrder(TreeNode treeNode){
    if(treeNode == null){
        return;
    }else{
        //首先把每個節(jié)點當成根節(jié)點輸出
        System.out.println("data:" + treeNode.data);
        //如果左節(jié)點有值吃嘿,則輸出左節(jié)點
        preOrder(treeNode.leftChild);
        ///如果右節(jié)點有值,輸出右節(jié)點梦重。
        preOrder(treeNode.rightChild);
    }
}
/**
 *  中序遍歷  遞歸
 * @param treeNode
 */
public void midOrder(TreeNode treeNode){
    if(treeNode == null){
        return;
    }else{
        //遞歸左子樹兑燥,直到左節(jié)點為null,return琴拧。
        midOrder(treeNode.leftChild);
        //輸出節(jié)點降瞳。
        System.out.println("data:" + treeNode.data);
        //遞歸右子樹
        midOrder(treeNode.rightChild);
    }
}
/**
 *  后序遍歷  遞歸
 * @param treeNode
 */
public void postOrder(TreeNode treeNode){
    if(treeNode == null){
        return;
    }else{
         //遞歸左子樹,直到左節(jié)點為null蚓胸,return挣饥。
        postOrder(treeNode.leftChild);
         //遞歸右子樹除师,直到左節(jié)點為null,return扔枫。
        postOrder(treeNode.rightChild);
        //輸出節(jié)點
        System.out.println("data:" + treeNode.data);
    }
}

二叉樹的遍歷理解了還是比較簡單的汛聚,下面來看一下二叉樹的重建。
已知 前序遍歷結(jié)果 :ABDGHCEIF 中序遍歷結(jié)果:GDHBAEICF茧吊,求二叉樹贞岭。

根據(jù)前序遍歷的特性八毯,可以知道A是根節(jié)點搓侄,這種在中序中可以得出 A節(jié)點左邊的就是左節(jié)點,右邊的就是右節(jié)點话速。這個規(guī)律可以使用遞歸

整個二叉樹的
左子樹的

下面就來看看重建二叉樹的遞歸代碼讶踪,具體的在代碼中都有注釋

/**
 * 已知前序和中序
 * @param pre       前序集合
 * @param startPre  前序集合起始點
 * @param endPre    前序集合總長度
 * @param in        中序集合
 * @param startIn   中序集合起始點
 * @param endIn     中序集合總長度
 * @return
 */
public TreeNode resetBinaryTreeString(String[] pre ,int startPre,int  endPre,String[] in,int startIn,int endIn){
    //如果開始大于結(jié)束,就說明這個樹已經(jīng)結(jié)束了
    if(startPre > endPre || startIn > endIn) {
        return null;
    }
    //把前序的頭節(jié)點放入樹中當根節(jié)點
    TreeNode node = new TreeNode(pre[startPre]);
    //遍歷中序的所有節(jié)點
    for (int i = startIn; i <= endIn; i++) {
        //找到根節(jié)點在中序中的位置
        if(pre[startPre] == in[i]){
           node.leftChild = resetBinaryTreeString(
                    pre,            //前序集合
                    startPre + 1,   //前序左子樹起始點: startPre + 1
                    startPre + (i - startIn),//左子樹長度:i是root節(jié)點在中序遍歷的位置,
                                             // (i - startIn)是中序遍歷中左子樹的長度泊交,
                                             // 所以左子樹的總長度:前序的 startPre + 中序遍歷中左子樹的長度
                    in,             //中序集合
                    startIn,        //中序左子樹的起始點:startIn
                    i - 1);         //中序左子樹的總長度:i -1
            node.rightChild = resetBinaryTreeString(
                    pre,             //前序集合
                    startPre + (i - startIn) + 1,//前序右子樹起始點: 前序左子樹的長度+1 , {startPre + (i - startIn)} + 1
                    endPre,          //前序右子樹總長度: 前序的總長度
                    in,              //中序集合
                    i + 1,           //中序右子樹的起始點:i+1
                    endIn);          //中序右子樹總長度: 中序的總長度
            break;
        }
    }
    return node;
}

這些是最近看二叉樹的一些收獲~簡單的分享一下乳讥。

下面貼上全部代碼:

public class BinaryTree {

private TreeNode root;

public BinaryTree() {
    root = new TreeNode(1, "A");
}

/**
 * 構(gòu)建二叉樹
 *                  A
 *              B       C
 *          D       E      F
 *      G       H     I
 */
public void createBinaryTree() {
    root = new TreeNode(1, "A");
    TreeNode treeNodeB = new TreeNode(2, "B");
    TreeNode treeNodeC = new TreeNode(3, "C");
    TreeNode treeNodeD = new TreeNode(4, "D");
    TreeNode treeNodeE = new TreeNode(5, "E");
    TreeNode treeNodeF = new TreeNode(6, "F");
    TreeNode treeNodeG = new TreeNode(7, "G");
    TreeNode treeNodeH = new TreeNode(8, "H");
    TreeNode treeNodeI = new TreeNode(9, "I");

    root.leftChild = treeNodeB;
    root.rightChild = treeNodeC;
    treeNodeB.leftChild = treeNodeD;
    treeNodeD.leftChild = treeNodeG;
    treeNodeD.rightChild = treeNodeH;
    treeNodeC.leftChild = treeNodeE;
    treeNodeC.rightChild = treeNodeF;
    treeNodeE.rightChild = treeNodeI;

}

/**
 *  前序遍歷  遞歸
 * @param treeNode
 */
public void preOrder(TreeNode treeNode){
    if(treeNode == null){
        return;
    }else{
        //首先輸出根節(jié)點。
        System.out.println("data:" + treeNode.data);
        //不斷的調(diào)用自身函數(shù)廓俭,把左節(jié)點輸出云石。
        preOrder(treeNode.leftChild);
        //根節(jié)點和左節(jié)點輸出完成,輸出右節(jié)點研乒。
        preOrder(treeNode.rightChild);
    }
}
/**
 *  中序遍歷  遞歸
 * @param treeNode
 */
public void midOrder(TreeNode treeNode){
    if(treeNode == null){
        return;
    }else{
        midOrder(treeNode.leftChild);
        System.out.println("data:" + treeNode.data);
        midOrder(treeNode.rightChild);
    }
}
/**
 *  后序遍歷  遞歸
 * @param treeNode
 */
public void postOrder(TreeNode treeNode){
    if(treeNode == null){
        return;
    }else{
        postOrder(treeNode.leftChild);
        postOrder(treeNode.rightChild);
        System.out.println("data:" + treeNode.data);
    }
}
/**
 *  前序遍歷  非遞歸
 * @param treeNode
 */
public void nonRecOrder(TreeNode treeNode){
    if(treeNode == null){
        return;
    }
    Stack<TreeNode> stack = new Stack<>();
    stack.push(treeNode);
    while (!stack.isEmpty()){
        //彈出根結(jié)點
        TreeNode node = stack.pop();
        System.out.println("data:" + node.data);
        //如果有右子結(jié)點汹忠,則壓入節(jié)點
        if(node.rightChild!=null){
            //如果左節(jié)點有值,則右節(jié)點在棧底雹熬,最后才會輸出
            stack.push(node.rightChild);
        }
        //如果有右左結(jié)點宽菜,則壓入節(jié)點
        if(node.leftChild!=null){
            //下次循環(huán)把左子節(jié)點當根節(jié)點繼續(xù)彈出
            stack.push(node.leftChild);
        }
    }
}

/**
 * 已知前序和中序
 * @param pre       前序集合
 * @param startPre  前序集合起始點
 * @param endPre    前序集合總長度
 * @param in        中序集合
 * @param startIn   中序集合起始點
 * @param endIn     中序集合總長度
 * @return
 */
public TreeNode resetBinaryTreeString(String[] pre ,int startPre,int  endPre,String[] in,int startIn,int endIn){
    //如果開始大于結(jié)束,就說明這個樹已經(jīng)結(jié)束了
    if(startPre > endPre || startIn > endIn) {
        return null;
    }
    //把前序的頭節(jié)點放入樹中當根節(jié)點
    TreeNode node = new TreeNode(pre[startPre]);
    //遍歷中序的所有節(jié)點
    for (int i = startIn; i <= endIn; i++) {
        //找到根節(jié)點在中序中的位置
        if(pre[startIn] == in[i]){
            node.leftChild = resetBinaryTreeString(
                    pre,            //前序集合
                    startPre + 1,   //前序左子樹起始點: startPre + 1
                    startPre + (i - startIn),//左子樹長度:i是root節(jié)點在中序遍歷的位置,
                    // (i - startIn)是中序遍歷中左子樹的長度竿报,
                    // 所以左子樹的總長度:前序的 startPre + 中序遍歷中左子樹的長度
                    in,             //中序集合
                    startIn,        //中序左子樹的起始點:startIn
                    i - 1);         //中序左子樹的總長度:i -1
            node.rightChild = resetBinaryTreeString(
                    pre,             //前序集合
                    startPre + (i - startIn) + 1,//前序右子樹起始點: 前序左子樹的長度+1 , {startPre + (i - startIn)} + 1
                    endPre,          //前序右子樹總長度: 前序的總長度
                    in,              //中序集合
                    i + 1,           //中序右子樹的起始點:i+1
                    endIn);          //中序右子樹總長度: 中序的總長度
            break;
        }
    }
    return node;
}

public static void main(String[] args){
    BinaryTree binaryTree= new BinaryTree();
    binaryTree.createBinaryTree();

    System.out.println("非遞歸前序");
    binaryTree.nonRecOrder(binaryTree.root);

    System.out.println("遞歸前序");
    binaryTree.preOrder(binaryTree.root);

    System.out.println("遞歸中序");
    binaryTree.midOrder(binaryTree.root);

    System.out.println("遞歸后序");
    binaryTree.postOrder(binaryTree.root);

    String[] pre = {"A","B","D","G","H","C","E","I","F"};

    String[] mid = {"G","D","H","B","A","E","I","C","F"};

    TreeNode root = binaryTree.resetBinaryTreeString(pre, 0, pre.length - 1, mid, 0, mid.length - 1);

    System.out.println("重建二叉樹前序");
    binaryTree.preOrder(root);
}

public class TreeNode {
    /**
     *
     */
    private int index;
    /**
     * 數(shù)據(jù)域
     */
    private String data;
    /**
     * 左子樹
     */
    private TreeNode leftChild;
    /**
     * 右子樹
     */
    private TreeNode rightChild;


    public int getIndex() {
        return index;
    }


    public void setIndex(int index) {
        this.index = index;
    }


    public String getData() {
        return data;
    }


    public void setData(String data) {
        this.data = data;
    }


    public TreeNode(int index, String data) {
        this.index = index;
        this.data = data;
        this.leftChild = null;
        this.rightChild = null;
    }

    public TreeNode(String data) {
        this.data = data;
    }
}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铅乡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子烈菌,更是在濱河造成了極大的恐慌阵幸,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芽世,死亡現(xiàn)場離奇詭異侨嘀,居然都是意外死亡,警方通過查閱死者的電腦和手機捂襟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門咬腕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人葬荷,你說我怎么就攤上這事涨共∨μ” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵举反,是天一觀的道長懊直。 經(jīng)常有香客問我,道長火鼻,這世上最難降的妖魔是什么室囊? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮魁索,結(jié)果婚禮上融撞,老公的妹妹穿的比我還像新娘。我一直安慰自己粗蔚,他們只是感情好尝偎,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹏控,像睡著了一般致扯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上当辐,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天抖僵,我揣著相機與錄音,去河邊找鬼缘揪。 笑死耍群,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的寺晌。 我是一名探鬼主播世吨,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼呻征!你這毒婦竟也來了耘婚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤陆赋,失蹤者是張志新(化名)和其女友劉穎沐祷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體攒岛,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡赖临,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了灾锯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兢榨。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吵聪,到底是詐尸還是另有隱情凌那,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布吟逝,位于F島的核電站帽蝶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏块攒。R本人自食惡果不足惜励稳,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望囱井。 院中可真熱鬧驹尼,春花似錦、人聲如沸琅绅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽千扶。三九已至,卻和暖如春骆捧,著一層夾襖步出監(jiān)牢的瞬間澎羞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工敛苇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留妆绞,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓枫攀,卻偏偏與公主長得像括饶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子来涨,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 樹的概述 樹是一種非常常用的數(shù)據(jù)結(jié)構(gòu)图焰,樹與前面介紹的線性表,棧蹦掐,隊列等線性結(jié)構(gòu)不同技羔,樹是一種非線性結(jié)構(gòu) 1.樹的定...
    Jack921閱讀 4,462評論 1 31
  • 基于樹實現(xiàn)的數(shù)據(jù)結(jié)構(gòu),具有兩個核心特征: 邏輯結(jié)構(gòu):數(shù)據(jù)元素之間具有層次關(guān)系卧抗; 數(shù)據(jù)運算:操作方法具有Log級的平...
    yhthu閱讀 4,281評論 1 5
  • 數(shù)據(jù)結(jié)構(gòu)和算法--二叉樹的實現(xiàn) 幾種二叉樹 1藤滥、二叉樹 和普通的樹相比,二叉樹有如下特點: 每個結(jié)點最多只有兩棵子...
    sunhaiyu閱讀 6,475評論 0 14
  • 一直以來社裆,我都很少使用也避免使用到樹和圖,總覺得它們神秘而又復(fù)雜拙绊,但是樹在一些運算和查找中也不可避免的要使用到,那...
    24K男閱讀 6,754評論 5 14
  • 這幾天開學,學校還在上課标沪,最近也是在找工作张漂,很多天都沒有更新文章,現(xiàn)在補一篇二叉樹的文章谨娜。 最近校招公司的筆試陸續(xù)...
    zero_sr閱讀 3,964評論 0 5