中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

java實現線索化二叉樹的前序、中序、后續的遍歷(完整代碼)

發布時間:2020-07-30 11:25:05 來源:網絡 閱讀:1360 作者:yushiwh 欄目:編程語言

java實現線索化二叉樹的前序、中序、后續的遍歷

比如創建一個二叉樹

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

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

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

    /**
     * //說明
     * //1. 如果leftType == 0 表示指向的是左子樹, 如果 1 則表示指向前驅結點
     * //2. 如果rightType == 0 表示指向是右子樹, 如果 1表示指向后繼結點
     */
    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;

    /**
     * 為了實現線索化,需要創建要給指向當前結點的前驅結點的指針
     * 在遞歸進行線索化時,pre 總是保留前一個結點
     */
    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() {
        //定義一個變量,存儲當前遍歷的結點,從root開始
        HeroNode node = root;
        while ( node != null ) {
            //循環的找到leftType == 1的結點,第一個找到就是8結點
            //后面隨著遍歷而變化,因為當leftType==1時,說明該結點是按照線索化
            //處理后的有效結點
            while ( node.getLeftType() == 0 ) {
                node = node.getLeft();
            }

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

        }
    }

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

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

    }

    /***********************遍歷線索化二叉樹結束**********************/

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

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

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

    }

    /**
     * 后序線索化
     * 變成數組后{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());

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

    /*********************線索化結束*********************************/

}
//測試二叉樹的遍歷

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

        //測試一把中序線索二叉樹的功能 以數組{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");

        //二叉樹,后面我們要遞歸創建, 現在簡單處理使用手動創建
        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號節點測試
        HeroNode leftNode = node5.getLeft();
        HeroNode rightNode = node5.getRight();
        System.out.println("10號結點的前驅結點是 =" + leftNode); //3
        System.out.println("10號結點的后繼結點是=" + rightNode); //1

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

        //******************前序*****************//
        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號節點測試
        HeroNode leftNodePre = node4.getLeft();
        HeroNode rightNodePre = node4.getRight();
        System.out.println("8號結點的前驅結點是 =" + leftNodePre); //3
        System.out.println("8號結點的后繼結點是=" + rightNodePre); //10

        HeroNode leftNodetenPre = node5.getLeft();
        HeroNode rightNodetenPre = node5.getRight();
        System.out.println("10號結點的前驅結點是 =" + leftNodetenPre); //8
        System.out.println("10號結點的后繼結點是=" + rightNodetenPre); //6

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

        //******************前序結束*****************//

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

        //如果是后序,需要創建二叉樹的時候,將parent進行保存。這個是用于后續二叉樹的遍歷的

        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號結點的前驅結點是 =" + leftNodeAfter); //null
        System.out.println("8號結點的后繼結點是=" + rightNodeAfter); //10

        HeroNode leftNodetenAfter = node5.getLeft();
        HeroNode rightNodetenAfter = node5.getRight();
        System.out.println("10號結點的前驅結點是 =" + leftNodetenAfter); //8
        System.out.println("10號結點的后繼結點是=" + rightNodetenAfter); //3

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

    }
}
  • 前序和中序差不多,比較好理解。后序比較難以理解。不過結合代碼后,斷點執行,看一下過程,對理解有好處。特別注意是后序遍歷要在二叉樹創建的時候,將parent進行保存設置。
向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

嵊泗县| 穆棱市| 泰顺县| 合阳县| 英超| 阿克陶县| 清河县| 秀山| 平塘县| 分宜县| 禄丰县| 无棣县| 锦州市| 南召县| 格尔木市| 伊春市| 河源市| 东台市| 枣阳市| 皮山县| 丹东市| 界首市| 孟州市| 荆州市| 襄城县| 汕头市| 利川市| 青阳县| 徐汇区| 葫芦岛市| 余干县| 东台市| 时尚| 宁南县| 沂南县| 丰原市| 西青区| 钟山县| 离岛区| 新野县| 乳源|