线索二叉树的建立及其遍历访问操作

  • 线索二叉树(中序),建立、访问

对于线索二叉树有先序、中序、后序三种,但是只有中序线索二叉树才能获得任意节点的前驱和后继节点(实际上二叉树任意节点的访问均是访问的某一棵树的根节点),那么只有中序遍历生成的中序线索二叉树是先访问左子树,根节点,右子树,而先序线索二叉树,是先访问根节点,所以只能从先序线索二叉树中得到节点的后继结点,同理只能从后序线索二叉树得到节点的前驱结点(最后访问根节点)。

package ccnu.offer.tree;

public class Demo03 {
    static ThreadNode pre = null;

    public static void main(String[] args) {
        ThreadNode root = createBiTreeByPreIn(new int[]{1, 2, 4, 3, 5}, new int[]{2, 4, 1, 5, 3}, 0, 0, 5);
        ThreadNode inRoot = createInThreadBiTree(root);
        inOrder(inRoot);
        System.out.println();
        inOrderByReverse(inRoot);
    }

    public static ThreadNode createBiTreeByPreIn(int[] pre, int[] in, int preBegin, int inBegin, int len){
        if(pre == null || in == null || pre.length != in.length || pre.length == 0 || in.length == 0){
            return null;
        }
        if(len <= 0){
            return null;
        }
        ThreadNode root = new ThreadNode(pre[preBegin]);
        int rootIndex;
        for(rootIndex = 0; in[rootIndex] != pre[preBegin]; rootIndex++);
        root.lchild = createBiTreeByPreIn(pre, in, preBegin + 1, inBegin, rootIndex - inBegin);
        root.rchild = createBiTreeByPreIn(pre, in, preBegin + (rootIndex - inBegin) + 1, rootIndex + 1, len - (rootIndex - inBegin) - 1);
        return root;
    }

    public static ThreadNode createInThreadBiTree(ThreadNode root){
//      ThreadNode pre = null;
        if(root != null){
            inThreadBiTree(root);
            pre.rtag = 1; // pre指向中序遍历的最后一个节点
            pre.rchild = null; // 没有后继节点
        }
        return root;
    }

    public static void inThreadBiTree(ThreadNode root){
        if(root != null){
            inThreadBiTree(root.lchild); // 第一步:线索化当前root的左子树
            // 第二步:访问当前root节点,如果当前root节点的左孩子为空,那么root的lchild就应该指向其中序序列前驱结点,同时如果root的前一个访问节点(中序序列)pre的rchild为空,那么pre的rchild就应该指向当前root
            if(root.lchild == null){
                root.ltag = 1; // root的lchild指向root的中序遍历前驱结点
                root.lchild = pre; // 当前节点的中序遍历前驱结点为中序遍历中的上一个访问的节点
            }
            if(pre != null && pre.rchild == null){ // pre != null 因为第一个访问节点没有前驱
                pre.rtag = 1; // 中序遍历上一个访问的节点pre指向其中序遍历的后继节点,即当前节点root
                pre.rchild = root;
            }
            pre = root; // 将刚才访问的节点保存,注意访问最后一个节点时,pre会指向它
            inThreadBiTree(root.rchild); // 第三步:线索化当前root的右子树
        }
    }

    public static void inOrder(ThreadNode root){ // 中序线索二叉树中序遍历
        if(root == null){
            return;
        }
        ThreadNode firstNode = firstNode(root);
        ThreadNode p = firstNode;
        while(p != null){
            System.out.print(p.data + " ");
            p = nextNode(p);
        }
    }

    public static void inOrderByReverse(ThreadNode root){
        if(root == null){
            return;
        }
        ThreadNode lastNode = lastNode(root);
        ThreadNode p = lastNode;
        while(p != null){
            System.out.print(p.data + " ");
            p = previousNode(p);
        }
    }

    public static ThreadNode firstNode(ThreadNode root){ // 得到中序遍历序列(以root作为根节点的树)的第一个节点--->没有左孩子的root子树就是中序序列的第一个节点
        if(root == null){
            return null;
        }
        while(root.ltag == 0){ // 存在左孩子
            root = root.lchild;
        }
        return root;
    }

    public static ThreadNode lastNode(ThreadNode root){ // 得到中序遍历序列(以root作为根节点的树)的最后一个节点--->没有右孩子的root子树就是中序序列的最后一个节点
        if(root == null){
            return null;
        }
        while(root.rtag == 0){ // root.rtag == 0不等价于root.rchild != null(不一定存在右孩子),因为rchild可能指向其后继节点
            root = root.rchild;
        }
        return root; // 
    }

    public static ThreadNode previousNode(ThreadNode p){
        if(p == null){
            return null;
        }
        if(p.ltag == 0){ // p存在左孩子
            return lastNode(p.lchild);
        }else{ // 没有左孩子
            return p.lchild; // lchild直接指向其前驱结点
        }
    }

    public static ThreadNode nextNode(ThreadNode p){ // 得到p节点的中序遍历序列的后继节点
        if(p == null){
            return null;
        }
        if(p.rtag == 0){ // 存在右孩子,那么找到p的后继节点等价于找到p.rchild的中序遍历的第一个节点
            return firstNode(p.rchild);
        }else{ // 木有右孩子,那么p节点的rchild直接就指向其后继节点
            return p.rchild;
        }
    }

    private static class ThreadNode{
        private int data;
        private int ltag = 0; // 默认lchild指向左孩子
        private int rtag = 0; // 默认rchild指向右孩子
        private ThreadNode lchild = null;
        private ThreadNode rchild = null;

        public ThreadNode(int data){
            this.data = data;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值