二叉树(四)| 线索化二叉树(java实现)

线索化二叉树

有n个结点的二叉链表中,每个结点有指向左右2个孩子的指针域,所以有2n个指针域,而n个结点的二叉树一共有n-1条分支线(根节点没有),也就是说,其实存在2n-(n-1) = n+1 个空指针域。空间十分浪费。在另一方面,我们对二叉树做中序遍历时,我只知道每个树结点的左右孩子是谁,却不知道该树结点的前驱和后继是谁。要想知道必须重新遍历一遍。

考虑在创建时就记住这些前驱和后继,那将会省去很多时间。如果树结点左右孩子都不为NULL ,如果采用中序遍历,那么该结点的前驱结点是不是左孩子,后继结点是不是右孩子?这就是为什么我们要采用中序遍历的形式来对二叉树进行线索化,那么我需要将孩子结点为NULL的树结点 空间利用起来!因为子节点虽然为NULL,但是都有前驱和后继结点,那么我们可以考虑将 lchild 放树节点的前驱结点,rchild放树结点的后继结点。那么这里我们是否要考虑区分lchild,rchild 是原有的子节点还是我们后续线索化添加的前驱后继结点呢?所以要在线索二叉树结点的数据结构都要体现呗。我们把这种指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的的二叉树就称为线索二叉树(Threaded Binary Tree)。

线索化二叉树示意图

 思路:如何线索化二叉树呢?采用中序遍历二叉树访问每一个树结点的时,只知道当前结点,如何知道前驱结点呢?能否考虑将刚刚访问的结点(pre)保存下来,每次访问树结点的时候,发现左孩子结点为NULL,就将其线索化!它的前驱结点是不是pre结点呢?!tree->lchild = pre;那个刚刚访问的结点 (pre)的右孩子结点 为NULL,那么将其线索化 pre 它的后继结点是不是当前结点呢?!pre->rchild = tree;

代码实现:

节点定义:

    //节点存储结构
   class Node {
        String data;        //数据域
        Node left;          //左指针域
        Node right;         //右指针域
        boolean isLeftThread = false;   //左指针域类型  false:指向子节点、true:前驱或后继线索
        boolean isRightThread = false;  //右指针域类型  false:指向子节点、true:前驱或后继线索
 
        Node(String data) {
            this.data = data;
        }
    }

构建线索化树:

public class LinearBinaryTree {
 
    private Node preNode;   //线索化时记录前一个节点
 
    //节点存储结构
    static class Node {
        String data;        //数据域
        Node left;          //左指针域
        Node right;         //右指针域
        boolean isLeftThread = false;   //左指针域类型  false:指向子节点、true:前驱或后继线索
        boolean isRightThread = false;  //右指针域类型  false:指向子节点、true:前驱或后继线索
 
        Node(String data) {
            this.data = data;
        }
    }
 
    /**
     * 通过数组构造一个二叉树(完全二叉树)  //递归创建二叉树
     * @param array
     * @param index
     * @return
     */
    static Node createBinaryTree(String[] array, int index) {
        Node node = null;
 
        if(index < array.length) {
            node = new Node(array[index]);
            node.left = createBinaryTree(array, index * 2 + 1);
            node.right = createBinaryTree(array, index * 2 + 2);
        }
 
        return node;
    }
 
    /**
     * 中序线索化二叉树
     * @param node  节点
     */
    void inThreadOrder(Node node) {
        if(node == null) {
            return;
        }
 
        //处理左子树
        inThreadOrder(node.left);
 
        //左指针为空,将左指针指向前驱节点
        if(node.left == null) {
            node.left = preNode;
            node.isLeftThread = true;
        }
 
        //前一个节点的后继节点指向当前节点
        if(preNode != null && preNode.right == null) {
            preNode.right = node;
            preNode.isRightThread = true;
        }
        preNode = node;
 
        //处理右子树
        inThreadOrder(node.right);
    }
 
    /**
     * 中序遍历线索二叉树,按照后继方式遍历(思路:找到最左子节点开始)
     * @param node
     */
    void inThreadList(Node node) {
        //1、找中序遍历方式开始的节点
        while(node != null && !node.isLeftThread) {
            node = node.left;
        }
 
        while(node != null) {
            System.out.print(node.data + ", ");
 
            //如果右指针是线索
            if(node.isRightThread) {
                node = node.right;
 
            } else {    //如果右指针不是线索,找到右子树开始的节点
                node = node.right;
                while(node != null && !node.isLeftThread) {
                    node = node.left;
                }
            }
        }
    }
 
    /**
     * 中序遍历线索二叉树,按照前驱方式遍历(思路:找到最右子节点开始倒序遍历)
     * @param node
     */
    void inPreThreadList(Node node) {
        //1、找最后一个节点
        while(node.right != null && !node.isRightThread) {
            node = node.right;
        }
 
        while(node != null) {
            System.out.print(node.data + ", ");
 
            //如果左指针是线索
            if(node.isLeftThread) {
                node = node.left;
 
            } else {    //如果左指针不是线索,找到左子树开始的节点
                node = node.left;
                while(node.right != null && !node.isRightThread) {
                    node = node.right;
                }
            }
        }
    }
 
    /**
     * 前序线索化二叉树
     * @param node
     */
    void preThreadOrder(Node node) {
        if(node == null) {
            return;
        }
 
        //左指针为空,将左指针指向前驱节点
        if(node.left == null) {
            node.left = preNode;
            node.isLeftThread = true;
        }
 
        //前一个节点的后继节点指向当前节点
        if(preNode != null && preNode.right == null) {
            preNode.right = node;
            preNode.isRightThread = true;
        }
 
        preNode = node;
 
        //处理左子树
        if(!node.isLeftThread) {
            preThreadOrder(node.left);
        }
 
        //处理右子树
        if(!node.isRightThread) {
            preThreadOrder(node.right);
        }
    }
 
    /**
     * 前序遍历线索二叉树(按照后继线索遍历)
     * @param node
     */
    void preThreadList(Node node) {
        while(node != null) {
 
            while(!node.isLeftThread) {
                System.out.print(node.data + ", ");
                node = node.left;
            }
 
            System.out.print(node.data + ", ");
            node = node.right;
        }
    }
 
    public static void main(String[] args) {
        String[] array = {"1", "2", "6", "9", "7", "14", "43", "8"};
        Node root = createBinaryTree(array, 0);
 
        LinearBinaryTree tree = new LinearBinaryTree();
        tree.inThreadOrder(root);
        System.out.println("中序按后继节点遍历线索二叉树结果:");
        tree.inThreadList(root);
        System.out.println("\n中序按前驱节点遍历线索二叉树结果:");
        tree.inPreThreadList(root);
 
        Node root2 = createBinaryTree(array, 0);
        LinearBinaryTree tree2 = new LinearBinaryTree();
        tree2.preThreadOrder(root2);
        tree2.preNode = null;
        System.out.println("\n前序按后继节点遍历线索二叉树结果:");
        tree.preThreadList(root2);
    }
}


 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值