二叉树前序后序中序互推总结

最近笔试题一直遇到这个题,今天就总结一下。本文主要回答以下几个问题(Java实现):

  1. 前序 + 中序 => 后序 + 层序
  2. 后序 + 中序 => 前序 + 层序
  3. 以上2个问题的2种解决办法(a. 手写出二叉树 b. 编程实现)

先看以下3种遍历的特点:

图中表示的是不同序中的根节点左子树右子树的位置

特点:

  1. 前序的第一个是root,后序的最后一个是root。
  2. 每种排序左子树右子树分布都是有规律的,见上图。
  3. 对于每一个子树(左/右)可以看成一个全新的树,仍然遵循上面的规律。

根据上面的特点,可以解决问题核心都是中序的结构特点:可以明确区分出左子树右子树的位置):
前+中=>后+层:

  1. 前序的第一个就是root,对应找到中序中根的位置,那么中序根前面的就是左子树,后面的就是右子树。
  2. 数一下左子树右子树的元素个数,对应在前序中找到左子树右子树的位置,如上图标示。
  3. 那么,下面对于左子树右子树又是一个全新的树,仍然按照以上规律,即在前序左子树L中,第一个元素B是root(同时也是父节点A的左节点),在前序右子树R中,第一个元素C是root(同时也是父节点A的右节点)……
  4. 重建出二叉树之后,后序/层序就是遍历就OK了。

后+中=>前+层:(类似上面的方法,就是后序的根节点在尾部,相应的左子树/右子树的根节点B/C在尾部,见上图所示。)

具体解决办法:

  1. 若是选择题,直接手写出二叉树,有了二叉树之后,什么序都秒秒钟的事。
  2. 若是编程题,那就要动手实现了,具体有2个思路:a. 直接递归打印各种序的排列(不用重建二叉树)。 b. 先重建出二叉树,再各种序的遍历打印。

实现:

  1. 手写:

    这里写图片描述

  2. 代码实现:

    直接递归打印,不用重建二叉树

    前序+中序=>后序


public class PreOrderAndInOrder {

    /**
     *
     * @param pre_bg_index 前序数组中,开始的索引(具体到左(右)子树中,数组开始索引+长度len精确指定了某一个左(右)子树)
     * @param in_bg_index 中序数组中,开始的索引
     * @param len 树(左(右)子树对应到数组中的长度)
     * @param pre_Order_Array 前序数组
     * @param in_Order_Array 中序数组 (这里一直把前序中序数组当做参数,是由于在递归过程中,没有动过数组,只是动态的去其中的某些项。)
     */
    public static void postOrderSolution(int pre_bg_index, int in_bg_index, int len, char[] pre_Order_Array, char[] in_Order_Array) {

        if (len == 0)
            return;

        char root = pre_Order_Array[pre_bg_index];
        if (len==1){//递归出口。
            System.out.print(root+" ");
            return;
        }

        int i = 0;
        while (root != in_Order_Array[in_bg_index + i]) {//找到中序根节点的位置。
            i++;
        }
        //左右中,标准的后序遍历的形式。
        postOrderSolution(pre_bg_index+1, in_bg_index, i, pre_Order_Array, in_Order_Array);
        postOrderSolution(pre_bg_index+i+1, in_bg_index+i+1, len-i-1, pre_Order_Array, in_Order_Array);
        System.out.print(root+" ");
    }

    public static void main(String[] args){

        char[] pre_Order_Array = {'A','B','D','E','H','C','F','G','I'};
        char[] in_Order_Array = {'D','B','H','E','A','F','C','G','I'};
        postOrderSolution(0, 0, pre_Order_Array.length, pre_Order_Array, in_Order_Array);
    }
}

后序+中序=>前序(与上面大体类似,就是后序的下标用于定位根节点的位置与前序不同)


public class InOrderAndPostOrder {

    /**
     *
     * @param post_end_index 后序数组中,结束的索引(具体到左(右)子树中,数组结束索引+长度len精确指定了某一个左(右)子树)
     * @param in_bg_index 中序数组中,开始的索引
     * @param len 树(左(右)子树对应到数组中的长度)
     * @param post_Order_Array 后序数组
     * @param in_Order_Array 中序数组 (这里一直把前序中序数组当做参数,是由于在递归过程中,没有动过数组,只是动态的去其中的某些项。)
     */
    public static void preOrderSolution(int post_end_index, int in_bg_index, int len, char[] post_Order_Array, char[] in_Order_Array) {

        if (len == 0)
            return;

        char root = post_Order_Array[post_end_index];
        if (len==1){//递归出口。
            System.out.print(root+" ");
            return;
        }

        int i = 0;
        while (root != in_Order_Array[in_bg_index + i]) {//找到中序根节点的位置。
            i++;
        }
        //中左右,标准的前序遍历的形式。注意左子树中后序结束的索引是从后往前减的,中间减掉的(len-i-1)是右子树对应的数组长度。
        System.out.print(root+" ");
        preOrderSolution(post_end_index-(len-i-1)-1, in_bg_index, i, post_Order_Array, in_Order_Array);
        preOrderSolution(post_end_index-1, in_bg_index+i+1, len-i-1, post_Order_Array, in_Order_Array);
    }

    public static void main(String[] args){

        char[] post_Order_Array = {'D','H','E','B','F','I','G','C','A'};
        char[] in_Order_Array = {'D','B','H','E','A','F','C','G','I'};
        preOrderSolution(post_Order_Array.length-1, 0, post_Order_Array.length, post_Order_Array, in_Order_Array);
    }
}

重建二叉树,然后各种序遍历输出,这里以 前序+中序=>后序+层序 为例:


public class TreeNode {

    char val;
    TreeNode left;
    TreeNode right;

    public TreeNode(char val) {
        this.val = val;
    }
}

public class ReConstructPreAndIn {
    /**
     * @param pre_bg_index    前序数组中,开始的索引(具体到左(右)子树中,数组开始索引+长度len精确指定了某一个左(右)子树)
     * @param in_bg_index     中序数组中,开始的索引
     * @param len             树(左(右)子树对应到数组中的长度)
     * @param pre_Order_Array 前序数组
     * @param in_Order_Array  中序数组 (这里一直把前序中序数组当做参数,是由于在递归过程中,没有动过数组,只是动态的去其中的某些项。)
     */
    public static TreeNode reConstruct(int pre_bg_index, int in_bg_index, int len, char[] pre_Order_Array, char[] in_Order_Array) {

        if (len == 0)
            return null;

        char root = pre_Order_Array[pre_bg_index];
        if (len == 1) {//递归出口。
            TreeNode newNode = new TreeNode(root);
            return newNode;
        }

        int i = 0;
        while (root != in_Order_Array[in_bg_index + i]) {//找到中序根节点的位置。
            i++;
        }
        TreeNode node = new TreeNode(root);
        node.left = reConstruct(pre_bg_index + 1, in_bg_index, i, pre_Order_Array, in_Order_Array);
        node.right = reConstruct(pre_bg_index + i + 1, in_bg_index + i + 1, len - i - 1, pre_Order_Array, in_Order_Array);
        return node;
    }

    //后序遍历
    public static void postOrderTransverse(TreeNode root) {
        if (root == null)
            return;

        if (root.left == null && root.right == null) {
            System.out.print(root.val + " ");
            return;
        }


        postOrderTransverse(root.left);
        postOrderTransverse(root.right);
        System.out.print(root.val + " ");
    }

    //层序遍历
    public static void levelOrderTransverse(TreeNode root){

        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        TreeNode node;
        while (!queue.isEmpty()){

            node = queue.peek();
            System.out.print(node.val+" ");
            queue.poll();
            if(node.left!=null)
                queue.add(node.left);
            if(node.right!=null)
                queue.add(node.right);
        }

    }

    public static void main(String[] args) {

        char[] pre_Order_Array = {'A', 'B', 'D', 'E', 'H', 'C', 'F', 'G', 'I'};
        char[] in_Order_Array = {'D', 'B', 'H', 'E', 'A', 'F', 'C', 'G', 'I'};
        TreeNode root = reConstruct(0, 0, pre_Order_Array.length, pre_Order_Array, in_Order_Array);
        postOrderTransverse(root);
        System.out.println();
        levelOrderTransverse(root);
    }
}

End

  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值