前序(后序)和中序遍历重建二叉树及二叉树遍历

1 篇文章 0 订阅

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof

解题思路:
前序遍历性质: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序。
中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序。

以题目示例为例:

前序遍历划分 [ 3 | 9 | 20 15 7 ]
中序遍历划分 [ 9 | 3 | 15 20 7 ]
根据以上性质,可得出以下推论:

前序遍历的首元素 为 树的根节点 node 的值。
在中序遍历中搜索根节点 node 的索引 ,可将 中序遍历 划分为 [ 左子树 | 根节点 | 右子树 ] 。
根据中序遍历中的左 / 右子树的节点数量,可将 前序遍历 划分为 [ 根节点 | 左子树 | 右子树 ] 。

贴张图:

class Solution_07 {
    public class ReBuildTree {
    private int[] po;
    private int[] last;
    private Map<Integer,Integer> dict;

    //前序和中序重构二叉树
    public TreeNode buildTree(int[] preOrder, int[] inOrder){
        if(preOrder == null || inOrder == null || preOrder.length == 0|| inOrder.length == 0 || preOrder.length != inOrder.length){
            return null;
        }
        po = preOrder;
        dict = new HashMap<Integer,Integer>(inOrder.length);
        for(int i = 0; i < inOrder.length; i++){
            dict.put(inOrder[i],i);
        }
        return reBuildTree(0, 0, inOrder.length - 1);
    }

    private TreeNode reBuildTree(int preRoot, int istart, int iend) {
        if(istart > iend){
            return null;
        }
        TreeNode treeNode = new TreeNode(po[preRoot]);
        int i = dict.get(po[preRoot]);
        int leftLen = i - istart;  //计算左子树长度,左子树的长度等于中序根节点的下标-中序起始节点的下标
        treeNode.left = reBuildTree(preRoot + 1, istart, i -1);
        treeNode.right = reBuildTree(preRoot + 1 + leftLen, i + 1, iend);
        return treeNode;
    }

    //后序和中序重构二叉树
    public TreeNode buildTreeByLast(int[] postOrder, int[] inOrder){
        if(postOrder == null || inOrder == null || postOrder.length == 0 || inOrder.length == 0 || postOrder.length != inOrder.length){
            return null;
        }
        po = postOrder;
        dict = new HashMap<Integer, Integer>(inOrder.length);
        for(int i = 0; i < inOrder.length; i++){
            dict.put(inOrder[i], i);
        }
        return reBuildTreeByLast(postOrder.length - 1, 0, inOrder.length - 1);
    }

    private TreeNode reBuildTreeByLast(int postRoot, int istart, int iend) {
        if(istart > iend || postRoot < 0){
            return null;
        }
        TreeNode treeNode = new TreeNode(po[postRoot]);
        int i = dict.get(po[postRoot]);
        int rightLen = iend - i;        //计算右子树的长度
        treeNode.left = reBuildTreeByLast(postRoot - rightLen - 1, istart, i - 1);
        treeNode.right = reBuildTreeByLast(postRoot - 1, i + 1, iend);
        return treeNode;
    }

}

}

比较难理解的点:

前序+中序重建二叉树递归函数中的参数含义:

“i”:根节点在中序遍历中的坐标

“preRoot + 1” :先序根节点坐标 + 1,即下次递归的左子树根节点坐标,也即左子树的起始坐标。

“preRoot + (i - istart)”:表示先序根节点坐标 + (中序根节点坐标 - 中序起始坐标)=左子树起始坐标 + (左子树长度),即前序遍历中左子树的终止坐标。 

“preRoot + 1 + (i - istart) ”:同理即左子树起始坐标 + 1 +(左子树长度),即右子树的根节点坐标(右子树的起始坐标)。

后序+中序重建二叉树基本与前序+中序一样,唯一不同的点是:

因为后序是从尾部开始的,所以在计算递归左子树时,需要用根节点坐标 - 右子树的长度 - 1  得到下次递归的左子树根节点的起始坐标。

知识点补充:

二叉树遍历

方法一:递归遍历

常用的一种方法。

方法二:利用栈迭代法

在利用栈遍历时,添加null是为了添加访问标记,当遇到null时,表示下一个节点即为要访问的节点,直接跳过null,弹出下一个节点即可。其实在前序遍历时,null的意义并不大,可以不加,只是为了前序,中序,后序的统一。

参考了该博主的解答

 以下为两种方法的Java实现:

import java.util.Stack;
class TreeNode{
    private int value;
    TreeNode left;
    TreeNode right;

    public TreeNode(int value){
        this.value = value;
    }

    @Override
    public String toString() {
        return "TreeNode{" +
                "value=" + value +
                '}';
    }
    /*
    递归先序遍历二叉树
     */
    public void preOrder(){
        System.out.println(this);
        if(this.left != null){
            this.left.preOrder();
        }
        if(this.right != null){
            this.right.preOrder();
        }
    }
    /*
    利用栈前序遍历二叉树
     */
    public void preOrderByStack(){
        System.out.println("利用栈前序遍历二叉树:");
        Stack<TreeNode> stack = new Stack<>();
        stack.push(this);
        while(!stack.isEmpty()){
            TreeNode treeNode = stack.pop();
            if(treeNode != null) {
                if (treeNode.right != null) {
                    stack.push(treeNode.right);
                }
                if(treeNode.left != null){
                    stack.push(treeNode.left);
                }
                stack.push(treeNode);
                stack.push(null);//加入访问标志
            }else{
                System.out.println(stack.pop());
            }
        }
    }
    /*
    递归中序遍历二叉树
     */
    public void inOrder(){
        if(this.left != null){
            this.left.inOrder();
        }
        System.out.println(this);
        if(this.right != null){
            this.right.inOrder();
        }
    }
    /*
    利用栈中序遍历二叉树
     */
    public void inOrderByStack(){
        System.out.println("利用栈中序遍历二叉树:");
        Stack<TreeNode> stack = new Stack<>();
        stack.push(this);
        while(!stack.isEmpty()){
            TreeNode treeNode = stack.pop();
            if(treeNode != null){
                if(treeNode.right != null){
                    stack.push(treeNode.right);
                }
                stack.push(treeNode);
                stack.push(null);//加入访问标志
                if(treeNode.left != null){
                    stack.push(treeNode.left);
                }
            }else{
                System.out.println(stack.pop());
            }
        }
    }
    /*
    递归后序遍历二叉树
     */
    public void postOrder(){
        if(this.left != null){
            this.left.postOrder();
        }
        if(this.right != null){
            this.right.postOrder();
        }
        System.out.println(this);
    }
    /*
    利用栈后序遍历二叉树
     */
    public void postOrderByStack(){
        System.out.println("利用栈后序遍历二叉树:");
        Stack<TreeNode> stack = new Stack<>();
        stack.push(this);
        while(!stack.isEmpty()){
            TreeNode treeNode = stack.pop();
            if(treeNode != null){
                stack.push(treeNode);
                stack.push(null);//加入访问标志
                if(treeNode.right != null){
                    stack.push(treeNode.right);
                }
                if(treeNode.left != null){
                    stack.push(treeNode.left);
                }
            }else{
                System.out.println(stack.pop());
            }
        }
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值