【剑指offer】重建二叉树&&二叉树的递归与非递归遍历

本文介绍如何根据给定的前序遍历和中序遍历结果重建二叉树,并提供递归与非递归的遍历实现。通过具体示例展示了二叉树的构建过程及多种遍历方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前序遍历:先根节点,左子结点,右子节点。
中序遍历:先左子节点,根节点,右子节点。
后序遍历:先左子结点,右子节点,根节点。

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

import java.util.Stack;

class TreeNode {
          int val;
          TreeNode left;
          TreeNode right;
          TreeNode(int x) { val = x; }
}
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        return reConTree(pre,0,pre.length-1,in,0,in.length-1);
    }
    public TreeNode reConTree(int[] pre,int preleft, int preright,int[] in,int inleft,int inright){
        if(preleft>preright||inleft>inright){
            return null;
        }
        TreeNode root = new TreeNode(pre[preleft]);
        for(int i=inleft;i<=inright;i++){
            if(pre[preleft]==in[i]){
                //构建左子树,区间长度为(i-inleft),先序区间为[preleft+1,preleft+(i-inleft)],中序区间为[inleft,i-1]
                root.left=reConTree(pre,preleft+1,preleft+(i-inleft),in,inleft,i-1);
                //构建右子树,区间长度为(inright-i),先序区间为[左子树右区间+1,先序右区间末],中序区间为[i+1,inright];
                root.right=reConTree(pre,preleft+(i-inleft)+1,preright,in,i+1,inright);
            }
        }
        return root;
    }

    /**
     * 递归遍历二叉树
     * @param node
     */

    public static void traverseBinTree(TreeNode node){
        if (node==null){
            return;
        }
        //System.out.println(node.val);          //前序遍历
        if(node.left!=null){
            traverseBinTree(node.left);
        }
        //System.out.println(node.val);          //中序遍历
        if(node.right!=null){
            traverseBinTree(node.right);
        }
        System.out.println(node.val);         //后序遍历
    }
    /**
     * 非递归版先序遍历
     * 
     * 即对于任一结点,其可看做是根结点,因此可以直接访问。
     * 访问完之后,若其左孩子不为空,按相同规则访问它的左子树
     * 当访问其左子树时,再访问它的右子树。
     * 
     * @param node
     */
    public static void preOrder(TreeNode node){
        Stack<TreeNode> tree = new Stack<TreeNode>();
        while(node!=null || !tree.empty()){
            while(node!=null){
                System.out.println(node.val);
                tree.push(node);
                node = node.left;
            }
            if(!tree.empty()){
                node = tree.pop();
                node = node.right;
            }
        }
    }
    /**
     * 非递归版中序遍历
     * 
     * 对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点。
     * 然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才进行访问。
     * 按相同的规则访问其右子树。
     * 
     * @param node
     */
    public static void inOrder(TreeNode node){
        Stack<TreeNode> tree = new Stack<TreeNode>();
        while(node!=null || !tree.empty()){
            while(node!=null){
                tree.push(node);
                node = node.left;
            }
            if(!tree.empty()){
                node = tree.pop();
                System.out.println(node.val);
                node = node.right;
            }
        }
    }
    /**
     * 非递归版后序遍历1
     * 
     * 要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。
     * 如果P不存在左孩子和右孩子,则可以直接访问它;
     * 或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。
     * 若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候。
     * 左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
     * 
     * @param node
     */
    public static void postOrder(TreeNode node){
        Stack<TreeNode> tree = new Stack<TreeNode>();
        TreeNode pre = null;  //前一个访问的节点
        TreeNode cur ;   //当前节点
        tree.push(node);
        while(!tree.empty()){
            cur = tree.peek();  //访问栈顶元素
            //如果当前结点没有孩子结点(情况一)或者孩子节点都已被访问过 (情况二)[pre!=null]
            if((cur.left==null&&cur.right==null)||(pre!=null&&(pre==cur.left||pre==cur.right))){
                System.out.println(cur.val);
                tree.pop();
                pre = cur;
            }else{
                //注意入栈先后顺序,右子节点点先入栈
                if(cur.right!=null){
                    tree.push(cur.right);
                }
                if(cur.left!=null){
                    tree.push(cur.left);
                }
            }
        }
    }
    /**
     * 非递归版后序遍历2
     * 
     * 对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点。
     * 此时该结点出现在栈顶,但是此时不能将其出栈并访问,因此其右孩子还为被访问。
     * 所以接下来按照相同的规则对其右子树进行相同的处理。
     * 当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。
     * 
     * @param node
     */
        public static void postOrder2(TreeNode node){
            Stack<TreeNode> tree = new Stack<TreeNode>();
            Stack<Integer> counts = new Stack<Integer>();
            Integer i = new Integer(1);
            while(node != null || !tree.empty()){
                while(node!=null){
                    tree.push(node);
                    counts.push(new Integer(0));
                    node = node.left;
                }
                while(!tree.empty()&&counts.peek().equals(i)){
                    counts.pop();
                    System.out.println(tree.pop().val);
                }
                if(!tree.empty()){
                    counts.pop();
                    counts.push(new Integer(1));  //标记有访问右孩子
                    node = tree.peek();
                    node = node.right;
                }
            }

        }
    public static void main(String[] args) {
        int[] pre = new int[]{1,2,4,7,3,5,6,8};
        int[] in = new int[]{4,7,2,1,5,3,8,6};
        TreeNode tree = new Solution().reConstructBinaryTree(pre, in);
        postOrder(tree);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值