1. 程式人生 > 其它 >線索二叉樹的前序中序後序線索化

線索二叉樹的前序中序後序線索化

13.2 線索化二叉樹

在我們構建成一個二叉樹的時候,我們發現葉子節點左右節點的指標並沒有完全利用上。

如果我們希望充分利用各個節點的左右指標,讓各個節點可以指向自己前驅,即父節點,於是線索化二叉樹就應運而生

特點

  1. n個節點的二叉連結串列中含有 n + 1公式 2*n-(n-1) = n + 1個空指標域。利用二叉連結串列中的空指標域,存放指向結點在某種遍歷次序下的前驅和後繼結點的指標(這種附加的指標稱為“線索”)
  2. 這種加上了線索的二叉連結串列稱為 線索連結串列,相應的二叉樹稱為 線索二叉樹(Threaded Binary Tree), 根據線索性質的不同,線索二叉樹可分為前序線索二叉樹、中序線索二叉樹、和後續線索二叉樹
    三種
  3. 一個結點的前一個結點,稱為前驅結點
  4. 一個結點的後一個結點,稱為後繼結點

需求:

​ 將下面的二叉樹進行中序線索二叉樹。中序遍歷的數列為{8, 3, 10, 1, 14, 6}

說明:當線索化二叉樹後,Node結點的屬性 left 和 right , 存在一下情況:

  1. left 指向的是左子樹,也可能是指向的前驅結點,比如 1 結點的 left 指向的左子樹,而 10 結點的 left 指向的就是前驅結點
  2. right 指向的是右子樹,也可能指向的是後繼結點,比如 1 節點的 right 指向的是右子樹,而 10 節點的 right 指向的就是後繼結點
package tree.threadedbinarytree;


public class ThreadedBinaryTreeDemo {
    public static void main(String[] args) {
        // 測試 把中序線索二叉樹
        Node1 root = new Node1(1, "12");
        Node1 node2 = new Node1(3, "13");
        Node1 node3 = new Node1(6, "16");
        Node1 node4 = new Node1(8, "18");
        Node1 node5 = new Node1(10, "114");
        Node1 node6 = new Node1(14, "114");

        // 二叉樹後面要遞迴建立,現在手動建立
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);

        // 測試線索化
        infixThreadedBinaryTree infixThreadedBinaryTree = new infixThreadedBinaryTree();
        infixThreadedBinaryTree.setRoot(root);
        infixThreadedBinaryTree.threadeNodes();

        // 測試:以10號結點為測試
        Node1 leftNode = node5.getLeft();
        Node1 rightNode = node5.getRight();
        System.out.println("10號結點的前驅結點時:" + leftNode);
        System.out.println("10號結點的後繼結點" + rightNode);

    }
}
// 線索化二叉樹 實現了線索化功能的二叉樹
class infixThreadedBinaryTree{
    private Node1 root;

    // 為了實現線索化,需要建立要給指向當前節點的前驅結點的指標
    // 在遞迴的進行線索時,pre 總是保留一個結點
    private Node1 pre = null;

    // 過載一把threadeNodes方法
    public void threadeNodes(){
        this.threadedNodes(root);
    }
    // 編寫二叉樹進行中序線索化的方法
    /**
     *
     * @param node 就是當前需要線索化的結點
     */
    public void threadedNodes(Node1 node){
        // 如果 node == null,不能進行線索化
        if (node == null){
            return;
        }
        // 1. 先線索化左子樹
        threadedNodes(node.getLeft());
        // 2. 線索化當前結點
        // 2.1 先處理當前節點的前驅結點
        if (node.getLeft() == null){
            // 讓當前節點的左指標指向前驅結點
            node.setLeft(pre);
            // 修改當前節點的左指標的型別,指向前驅結點
            node.setLeftType(1);
        }
        // 2.2 處理當前節點的後繼結點
        if (pre != null && pre.getRight() == null){
            // 讓前驅結點的右指標指向當前結點
            pre.setRight(node);
            // 修改前驅結點的右指標型別
            pre.setLeftType(1);
        }
        // 沒處理一個節點後,讓當前結點時下一個節點的前驅結點
        pre = node;
        // 3. 再線索化右子樹
        threadedNodes(node.getRight());
    }
    public Node1 getRoot() {
        return root;
    }

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


}
// 先建立節點
class Node1{
    private int id;
    private String name;
    private Node1 left;
    private Node1 right;
    // 說明
    // 1. 如果leftType == 0 表示的指向的是左子樹,如果1則表示指向前驅結點
    // 2. 如果rightType == 0 表示指向的是右子樹,如果1則表示指向的是後繼結點
    private int leftType;
    private int rightType;

    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    public Node1(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Node1 getLeft() {
        return left;
    }

    public void setLeft(Node1 left) {
        this.left = left;
    }

    public Node1 getRight() {
        return right;
    }

    public void setRight(Node1 right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "Node1{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

}


前序線索化,後序線索化

注意,前序線索化要新增判斷條件,否則就會陷入無限迭代,無法跳出導致棧滿,判斷條件就是我們宣告的 leftTyperightType的值,如果為1,則我們進行遞迴,如果不為1,則我們不進行遞迴,否則會進入死迴圈。

package tree.threadedbinarytree;


public class ThreadedBinaryTreeDemo {
    public static void main(String[] args) {
        // 測試 把中序線索二叉樹
        Node1 root = new Node1(1, "12");
        Node1 node2 = new Node1(3, "13");
        Node1 node3 = new Node1(6, "16");
        Node1 node4 = new Node1(8, "18");
        Node1 node5 = new Node1(10, "114");
        Node1 node6 = new Node1(14, "114");

        // 二叉樹後面要遞迴建立,現在手動建立
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);

        // 測試中序線索化
        infixThreadedBinaryTree infixThreadedBinaryTree = new infixThreadedBinaryTree();
//        infixThreadedBinaryTree.setRoot(root);
//        infixThreadedBinaryTree.threadeNodes();
//
//        // 測試:以10號結點為測試
//        Node1 leftNode = node5.getLeft();
//        Node1 rightNode = node5.getRight();
//        System.out.println("10號結點的前驅結點時:" + leftNode);
//        System.out.println("10號結點的後繼結點" + rightNode);

        // 測試前序線索化
        infixThreadedBinaryTree.setRoot(root);
        infixThreadedBinaryTree.postNodes();

        // 測試:以10號結點為測試
        Node1 leftNode = node5.getLeft();
        Node1 rightNode = node5.getRight();
        System.out.println("10號結點的前驅結點時:" + leftNode);
        System.out.println("10號結點的後繼結點" + rightNode);
    }
}
// 線索化二叉樹 實現了線索化功能的二叉樹
class infixThreadedBinaryTree{
    private Node1 root;

    // 為了實現線索化,需要建立要給指向當前節點的前驅結點的指標
    // 在遞迴的進行線索時,pre 總是保留一個結點
    private Node1 pre = null;

    // 過載一把threadeNodes方法
    public void threadeNodes(){
        this.threadedNodes(root);
    }
    // 編寫二叉樹進行中序線索化的方法
    /**
     *
     * @param node 就是當前需要線索化的結點
     */
    public void threadedNodes(Node1 node){
        // 如果 node == null,不能進行線索化
        if (node == null){
            return;
        }
        // 1. 先線索化左子樹
        threadedNodes(node.getLeft());
        // 2. 線索化當前結點
        // 2.1 先處理當前節點的前驅結點
        if (node.getLeft() == null){
            // 讓當前節點的左指標指向前驅結點
            node.setLeft(pre);
            // 修改當前節點的左指標的型別,指向前驅結點
            node.setLeftType(1);
        }
        // 2.2 處理當前節點的後繼結點
        if (pre != null && pre.getRight() == null){
            // 讓前驅結點的右指標指向當前結點
            pre.setRight(node);
            // 修改前驅結點的右指標型別
            pre.setLeftType(1);
        }
        // 沒處理一個節點後,讓當前結點時下一個節點的前驅結點
        pre = node;
        // 3. 再線索化右子樹
        threadedNodes(node.getRight());
    }

    public void preNodes(){
        this.preNodes(root);
    }
    // 前序線索化
    public void preNodes(Node1 node){
        if (node == null){
            return;
        }
        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() == 0){
            preNodes(node.getLeft());
        }

        if (node.getRightType() == 0){
            preNodes(node.getRight());
        }

    }
    // 後序線索化
    public void postNodes(){
        this.postNodes(root);
    }
    public void postNodes(Node1 node){
        if (node == null){
            return;
        }
        postNodes(node.getLeft());
        postNodes(node.getRight());
        if (node.getLeft() == null){
            node.setLeft(pre);
            node.setLeftType(1);
        }
        if ( pre != null && pre.getRight() == null){
            pre.setRight(node);
            pre.setRightType(1);
        }

        pre = node;
    }
    public void setRoot(Node1 root) {
        this.root = root;
    }


}
// 先建立節點
class Node1{
    private int id;
    private String name;
    private Node1 left;
    private Node1 right;
    // 說明
    // 1. 如果leftType == 0 表示的指向的是左子樹,如果1則表示指向前驅結點
    // 2. 如果rightType == 0 表示指向的是右子樹,如果1則表示指向的是後繼結點
    private int leftType;
    private int rightType;

    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    public Node1(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Node1 getLeft() {
        return left;
    }

    public void setLeft(Node1 left) {
        this.left = left;
    }

    public Node1 getRight() {
        return right;
    }

    public void setRight(Node1 right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "Node1{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

}