溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

java實(shí)現(xiàn)線索化二叉樹的前序、中序、后續(xù)的遍歷(完整代碼)

發(fā)布時(shí)間:2020-07-30 11:25:05 來源:網(wǎng)絡(luò) 閱讀:1351 作者:yushiwh 欄目:編程語言

java實(shí)現(xiàn)線索化二叉樹的前序、中序、后續(xù)的遍歷

比如創(chuàng)建一個(gè)二叉樹

           1
       /       \
      3        6      
     / \         /       
    8  10  14
  • 線索化二叉樹幾個(gè)概念

    • n個(gè)節(jié)點(diǎn)的二叉鏈表中含有n+1
      【公式2n-(n-1)=n+1】個(gè)空指針域。利用二叉鏈表中的空指針域,存放指向該節(jié)點(diǎn)在某種遍歷次序下的前驅(qū)和后繼節(jié)點(diǎn)的指針(這種附加指針成為線索)。
      如下面的就是6+1=7個(gè)空指針域 (8,10,14各有連個(gè)指針沒有指向 6有一個(gè))
    • 加上了線索的二叉鏈表稱為線索鏈表,相應(yīng)的二叉樹稱為線索二叉樹。分為前序線索二叉樹、中序線索二叉樹、后序線索二叉樹
    • 一個(gè)節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn),稱為前驅(qū)節(jié)點(diǎn)
    • 一個(gè)節(jié)點(diǎn)的后一個(gè)節(jié)點(diǎn),稱為后繼節(jié)點(diǎn)
    • 線索化后的二叉樹,節(jié)點(diǎn)可能指向的是前驅(qū)或者后繼節(jié)點(diǎn),也有可能指向的是本身的二叉樹的節(jié)點(diǎn)
  • 前序、中序、后序線索化和遍歷
//創(chuàng)建樹的節(jié)點(diǎn)
/**
 * 〈節(jié)點(diǎn)定義〉
 *
 * @author nick
 * @create 2019/9/17
 * @since 1.0.0
 */
@Data
public class HeroNode {
    private int no;
    private String name;
    /**
     * //默認(rèn)null
     */
    private HeroNode left;
    /**
     * //默認(rèn)null
     */
    private HeroNode right;

    /**
     * //父節(jié)點(diǎn)的指針(為了后序線索化使用)
     */
    private HeroNode parent;

    /**
     * //說明
     * //1. 如果leftType == 0 表示指向的是左子樹, 如果 1 則表示指向前驅(qū)結(jié)點(diǎn)
     * //2. 如果rightType == 0 表示指向是右子樹, 如果 1表示指向后繼結(jié)點(diǎn)
     */
    private int leftType;
    private int rightType;

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }

}
/**
 * 〈線索化二叉樹〉
 * 1
 * /   \
 * 3     6
 * / \   /
 * 8  10 14
 *
 * @author nick
 * @create 2019/9/17
 * @since 1.0.0
 */
public class ThreadedBinaryTree {
    private HeroNode root;

    /**
     * 為了實(shí)現(xiàn)線索化,需要?jiǎng)?chuàng)建要給指向當(dāng)前結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)的指針
     * 在遞歸進(jìn)行線索化時(shí),pre 總是保留前一個(gè)結(jié)點(diǎn)
     */
    private HeroNode pre = null;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    /**
     * 重載一把threadedNodes方法
     */
    public void threadedNodes() {
        this.threadedNodes(root);
    }

    /**
     * 重載一把threadedNodesPre方法
     */
    public void threadedNodesPre() {
        this.threadedNodesPre(root);
    }

    /**
     * 重載一把threadedNodesAfter方法
     */
    public void threadedNodesAfter() {
        this.threadedNodesAfter(root);
    }

    /***********************遍歷線索化二叉樹開始**********************/

    /**
     * 中序遍歷線索化二叉樹的方法
     * <p>
     */

    public void threadedList() {
        //定義一個(gè)變量,存儲(chǔ)當(dāng)前遍歷的結(jié)點(diǎn),從root開始
        HeroNode node = root;
        while ( node != null ) {
            //循環(huán)的找到leftType == 1的結(jié)點(diǎn),第一個(gè)找到就是8結(jié)點(diǎn)
            //后面隨著遍歷而變化,因?yàn)楫?dāng)leftType==1時(shí),說明該結(jié)點(diǎn)是按照線索化
            //處理后的有效結(jié)點(diǎn)
            while ( node.getLeftType() == 0 ) {
                node = node.getLeft();
            }

            //打印當(dāng)前這個(gè)結(jié)點(diǎn)
            System.out.println(node);
            //如果當(dāng)前結(jié)點(diǎn)的右指針指向的是后繼結(jié)點(diǎn),就一直輸出
            while ( node.getRightType() == 1 ) {
                //獲取到當(dāng)前結(jié)點(diǎn)的后繼結(jié)點(diǎn)
                node = node.getRight();
                System.out.println(node);
            }
            //替換這個(gè)遍歷的結(jié)點(diǎn)
            node = node.getRight();

        }
    }

    /**
     * 前序線索化二叉樹遍歷方法
     * 1
     * /   \
     * 3     6
     * / \   /
     * 8  10 14
     * <p>
     * {1,3,8,10,6,14}
     */
    public void threadedListPre() {
        //定義一個(gè)變量,存儲(chǔ)當(dāng)前遍歷的結(jié)點(diǎn),從root開始
        HeroNode node = root;
        while ( node != null ) {
            while ( node.getLeftType() == 0 ) {
                //如果是葉子節(jié)點(diǎn),非前驅(qū)節(jié)點(diǎn),打印當(dāng)前這個(gè)結(jié)點(diǎn)
                System.out.print(node + ",");
                node = node.getLeft();
            }
            System.out.print(node + ",");
            //替換這個(gè)遍歷的結(jié)點(diǎn)
            node = node.getRight();
        }
    }

    /**
     * 后序線索化二叉樹遍歷方法
     * <p>
     * 注意后序有點(diǎn)復(fù)雜,需要建立二叉樹的時(shí)候,將節(jié)點(diǎn)的parent進(jìn)行賦值,否則不能遍歷成功
     * 1
     * /   \
     * 3     6
     * / \   /
     * 8  10 14
     * <p>
     * {8,10,3,1,14,6}
     * 1. 如果leftType == 0 表示指向的是左子樹, 如果 1 則表示指向前驅(qū)結(jié)點(diǎn)
     * 2. 如果rightType == 0 表示指向是右子樹, 如果 1表示指向后繼結(jié)點(diǎn)
     */
    public void threadedListAfter() {
        //1、找后序遍歷方式開始的節(jié)點(diǎn)
        HeroNode node = root;
        while ( node != null && node.getLeftType() == 0 ) {
            node = node.getLeft();
        }
        while ( node != null ) {
            //右節(jié)點(diǎn)是線索
            if (node.getRightType() == 1) {
                System.out.print(node + ", ");
                pre = node;
                node = node.getRight();
            } else {
                //如果上個(gè)處理的節(jié)點(diǎn)是當(dāng)前節(jié)點(diǎn)的右節(jié)點(diǎn)
                if (node.getRight() == pre) {
                    System.out.print(node + ", ");
                    if (node == root) {
                        return;
                    }
                    pre = node;
                    node = node.getParent();
                } else {    //如果從左節(jié)點(diǎn)的進(jìn)入則找到有子樹的最左節(jié)點(diǎn)
                    node = node.getRight();
                    while ( node != null && node.getLeftType() == 0 ) {
                        node = node.getLeft();
                    }
                }
            }
        }

    }

    /***********************遍歷線索化二叉樹結(jié)束**********************/

    /****************線索化二叉樹開始********************************/

    /**
     * 中序線索化
     * 得到的數(shù)組{8, 3, 10, 1, 14, 6}
     * 1
     * /   \
     * 3     6
     * / \   /
     * 8  10 14
     *
     * @param node 就是當(dāng)前需要線索化的結(jié)點(diǎn)
     */
    public void threadedNodes(HeroNode node) {
        //如果node==null, 不能線索化
        if (node == null) {
            return;
        }
        //(一)先線索化左子樹
        threadedNodes(node.getLeft());
        //(二)線索化當(dāng)前結(jié)點(diǎn)[有難度]
        //處理當(dāng)前結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)
        //以8結(jié)點(diǎn)來理解
        //8結(jié)點(diǎn)的.left = null , 8結(jié)點(diǎn)的.leftType = 1
        if (null == node.getLeft()) {
            //讓當(dāng)前結(jié)點(diǎn)的左指針指向前驅(qū)結(jié)點(diǎn)
            node.setLeft(pre);
            //修改當(dāng)前結(jié)點(diǎn)的左指針的類型,指向前驅(qū)結(jié)點(diǎn)
            node.setLeftType(1);
        }
        //處理后繼結(jié)點(diǎn),是下一次進(jìn)行處理,有點(diǎn)不好理解
        if (pre != null && pre.getRight() == null) {
            //讓前驅(qū)結(jié)點(diǎn)的右指針指向當(dāng)前結(jié)點(diǎn)
            pre.setRight(node);
            //修改前驅(qū)結(jié)點(diǎn)的右指針類型
            pre.setRightType(1);
        }
        //!!! 每處理一個(gè)結(jié)點(diǎn)后,讓當(dāng)前結(jié)點(diǎn)是下一個(gè)結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)
        pre = node;
        //(三)在線索化右子樹
        threadedNodes(node.getRight());
    }

    /**
     * 前序線索化
     * 變成數(shù)組后{1,3,8,10,6,14}
     * 1
     * /   \
     * 3     6
     * / \   /
     * 8  10 14
     *
     * @param node 就是當(dāng)前需要線索化的結(jié)點(diǎn)
     */
    public void threadedNodesPre(HeroNode node) {
        //如果node==null, 不能線索化
        if (node == null) {
            return;
        }
        //左指針為空,將左指針指向前驅(qū)節(jié)點(diǎn)
        //8結(jié)點(diǎn)的.left = 上一個(gè)節(jié)點(diǎn) , 8結(jié)點(diǎn)的.leftType = 1
        if (node.getLeft() == null) {
            //讓當(dāng)前結(jié)點(diǎn)的左指針指向前驅(qū)結(jié)點(diǎn)
            node.setLeft(pre);
            //修改當(dāng)前結(jié)點(diǎn)的左指針的類型,指向前驅(qū)結(jié)點(diǎn)
            node.setLeftType(1);
        }
        //處理后繼結(jié)點(diǎn),是下一次進(jìn)行處理,有點(diǎn)不好理解
        if (pre != null && pre.getRight() == null) {
            //讓前驅(qū)結(jié)點(diǎn)的右指針指向當(dāng)前結(jié)點(diǎn)
            pre.setRight(node);
            //修改前驅(qū)結(jié)點(diǎn)的右指針類型
            pre.setRightType(1);
        }
        //!!! 每處理一個(gè)結(jié)點(diǎn)后,讓當(dāng)前結(jié)點(diǎn)是下一個(gè)結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)
        pre = node;
        //(一)先線索化左子樹
        if (node.getLeftType() != 1) {
            threadedNodesPre(node.getLeft());
        }
        //(三)再線索化右子樹
        if (node.getRightType() != 1) {
            threadedNodesPre(node.getRight());
        }

    }

    /**
     * 后序線索化
     * 變成數(shù)組后{8,10,3,1,14,6}
     *
     * @param node
     */
    public void threadedNodesAfter(HeroNode node) {
        //如果node==null, 不能線索化
        if (node == null) {
            return;
        }

        //(一)先線索化左子樹
        threadedNodesAfter(node.getLeft());
        //(三)再線索化右子樹
        threadedNodesAfter(node.getRight());

        //左指針為空,將左指針指向前驅(qū)節(jié)點(diǎn)
        //8結(jié)點(diǎn)的.left = 上一個(gè)節(jié)點(diǎn) , 8結(jié)點(diǎn)的.leftType = 1
        if (node.getLeft() == null) {
            //讓當(dāng)前結(jié)點(diǎn)的左指針指向前驅(qū)結(jié)點(diǎn)
            node.setLeft(pre);
            //修改當(dāng)前結(jié)點(diǎn)的左指針的類型,指向前驅(qū)結(jié)點(diǎn)
            node.setLeftType(1);
        }
        //處理后繼結(jié)點(diǎn),是下一次進(jìn)行處理,有點(diǎn)不好理解
        if (pre != null && pre.getRight() == null) {
            //讓前驅(qū)結(jié)點(diǎn)的右指針指向當(dāng)前結(jié)點(diǎn)
            pre.setRight(node);
            //修改前驅(qū)結(jié)點(diǎn)的右指針類型
            pre.setRightType(1);
        }
        //!!! 每處理一個(gè)結(jié)點(diǎn)后,讓當(dāng)前結(jié)點(diǎn)是下一個(gè)結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)
        pre = node;
    }

    /*********************線索化結(jié)束*********************************/

}
//測試二叉樹的遍歷

/**
 * 〈線索化二叉樹測試〉
 *
 * @author nick
 * @create 2019/9/17
 * @since 1.0.0
 */
public class ThreadedBinaryTreeTest extends Tester {
    @Test
    public void testPolandNotation() throws Exception {

        //測試一把中序線索二叉樹的功能 以數(shù)組{8, 3, 10, 1, 14, 6}為例

        /**
         *          1
         *        /   \
         *       3     6
         *      / \   /
         *     8  10 14
         */

        HeroNode root = new HeroNode(1, "java");
        HeroNode node2 = new HeroNode(3, "C#");
        HeroNode node3 = new HeroNode(6, "Python");
        HeroNode node4 = new HeroNode(8, "C++");
        HeroNode node5 = new HeroNode(10, "GO");
        HeroNode node6 = new HeroNode(14, "Dephi");

        //二叉樹,后面我們要遞歸創(chuàng)建, 現(xiàn)在簡單處理使用手動(dòng)創(chuàng)建
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);

        //*************測試中序線索化***************//

        System.out.println("==========中序線索化開始=============");
        System.out.println("{8, 3, 10, 1, 14, 6}");
        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        threadedBinaryTree.setRoot(root);
        threadedBinaryTree.threadedNodes();

        //測試: 以10號(hào)節(jié)點(diǎn)測試
        HeroNode leftNode = node5.getLeft();
        HeroNode rightNode = node5.getRight();
        System.out.println("10號(hào)結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)是 =" + leftNode); //3
        System.out.println("10號(hào)結(jié)點(diǎn)的后繼結(jié)點(diǎn)是=" + rightNode); //1

        //當(dāng)線索化二叉樹后,能在使用原來的遍歷方法
        //threadedBinaryTree.infixOrder();
        System.out.println("中序使用線索化的方式遍歷 線索化二叉樹");
        threadedBinaryTree.threadedList(); // 8, 3, 10, 1, 14, 6
        //********************中序結(jié)束******************//

        //******************前序*****************//
        System.out.println("==========前序線索化開始=============");
        System.out.println("{1,3,8,10,6,14}");

        //前序:{1,3,8,10,6,14}
        ThreadedBinaryTree threadedBinaryTreePre = new ThreadedBinaryTree();
        threadedBinaryTreePre.setRoot(root);
        threadedBinaryTreePre.threadedNodesPre();

        //測試: 以10號(hào)節(jié)點(diǎn)測試
        HeroNode leftNodePre = node4.getLeft();
        HeroNode rightNodePre = node4.getRight();
        System.out.println("8號(hào)結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)是 =" + leftNodePre); //3
        System.out.println("8號(hào)結(jié)點(diǎn)的后繼結(jié)點(diǎn)是=" + rightNodePre); //10

        HeroNode leftNodetenPre = node5.getLeft();
        HeroNode rightNodetenPre = node5.getRight();
        System.out.println("10號(hào)結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)是 =" + leftNodetenPre); //8
        System.out.println("10號(hào)結(jié)點(diǎn)的后繼結(jié)點(diǎn)是=" + rightNodetenPre); //6

        System.out.println("前序使用線索化的方式遍歷 線索化二叉樹");
        threadedBinaryTreePre.threadedListPre();//{1,3,8,10,6,14}

        //******************前序結(jié)束*****************//

        //******************后序*****************//

        //如果是后序,需要?jiǎng)?chuàng)建二叉樹的時(shí)候,將parent進(jìn)行保存。這個(gè)是用于后續(xù)二叉樹的遍歷的

        node2.setParent(root);
        node3.setParent(root);
        node4.setParent(node2);
        node5.setParent(node2);
        node6.setParent(node3);

        System.out.println("==========后序線索化開始=============");
        System.out.println("{8,10,3,14,6,1}");
        //后序:{8,10,3,14,6,1}
        ThreadedBinaryTree threadedBinaryTreeAfter = new ThreadedBinaryTree();
        threadedBinaryTreeAfter.setRoot(root);
        threadedBinaryTreeAfter.threadedNodesAfter();

        HeroNode leftNodeAfter = node4.getLeft();
        HeroNode rightNodeAfter = node4.getRight();
        System.out.println("8號(hào)結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)是 =" + leftNodeAfter); //null
        System.out.println("8號(hào)結(jié)點(diǎn)的后繼結(jié)點(diǎn)是=" + rightNodeAfter); //10

        HeroNode leftNodetenAfter = node5.getLeft();
        HeroNode rightNodetenAfter = node5.getRight();
        System.out.println("10號(hào)結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)是 =" + leftNodetenAfter); //8
        System.out.println("10號(hào)結(jié)點(diǎn)的后繼結(jié)點(diǎn)是=" + rightNodetenAfter); //3

        System.out.println("后序使用線索化的方式遍歷 線索化二叉樹");
        threadedBinaryTreeAfter.threadedListAfter();//{8,10,3,14,6,1}

    }
}
  • 前序和中序差不多,比較好理解。后序比較難以理解。不過結(jié)合代碼后,斷點(diǎn)執(zhí)行,看一下過程,對(duì)理解有好處。特別注意是后序遍歷要在二叉樹創(chuàng)建的時(shí)候,將parent進(jìn)行保存設(shè)置。
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI