zoukankan      html  css  js  c++  java
  • 重建二叉树


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


    做这一题需要对二叉树有一定了解,不了解的同学建议先移步

    点击这里学习二叉树

    根据中序遍历和前序遍历的规则,可以确定二叉树,具体过程为:

    • 根据前序序列第一个结点确定根结点
    • 根据根结点在中序序列中的位置分割出左右两个子序列
    • 对左子树和右子树分别递归使用同样的方法继续分解

    例如,对于这道题目而言:

    前序序列 {1,2,4,7,3,5,6,8} = pre

    中序序列 {4,7,2,1,5,3,8,6} = in

    根据当前前序序列的第一个结点确定根结点,为 1

    找到 1 在中序遍历序列中的位置,为 in[3]

    切割左右子树,则 in[3] 前面的为左子树, in[3] 后面的为右子树,则切割后的左子树前序序列为:{2,4,7},切割后的左子树中序序列为:{4,7,2};切割后的右子树前序序列为:{3,5,6,8},切割后的右子树中序序列为:{5,3,8,6}

    接下来,对子树分别使用同样的方法分解,递归即可

    import java.util.Arrays;
    public class Solution {
        public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
            if (pre.length == 0 || in.length == 0) {
                return null;
            }
            TreeNode treeNode = new TreeNode(pre[0]);
            for (int i = 0; i < in.length; i++) {
                if (in[i] == pre[0]) {
                    treeNode.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i + 1), Arrays.copyOfRange(in, 0, i));
                    treeNode.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length), Arrays.copyOfRange(in, i + 1, in.length));
                    break;
                }
            }
            return treeNode;
        }
    }
    

    这里使用了库函数辅助完成数组分割,也可以自己实现

    public class Solution {
        
        public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
            if(pre.length == 0 || in.length == 0) {
                return null;
            }
            // 记录中序数组中根结点的下标
            int i = 0;
            // 保存根结点
            TreeNode node = new TreeNode(pre[0]);
            // 找到根结点下标
            while(i < in.length) {
                if(in[i] == node.val) {
                    break;
                }
                i++;
            }
            // 四个数组分别保存前序左子树、前序右子树、中序左子树、中序右子树
            int[] preLeft = new int[i];
            int[] inLeft = new int[i];
            int[] preRight = new int[pre.length - i - 1];
            int[] inRight = new int[in.length - i - 1];
            // 为四个数组赋值
            for(int j = 0; j < in.length; j++) {
            	// j < i,构造前序左子树和中序左子树
                if(j < i) {
                    preLeft[j] = pre[j + 1];
                    inLeft[j] = in[j];
                }
                // j > i,构造前序右子树和中序右子树
                if(j > i) {
                    preRight[j - i - 1] = pre[j];
                    inRight[j - i - 1] = in[j];
                }
            }
            // 递归调用
            node.left = reConstructBinaryTree(preLeft, inLeft);
            node.right = reConstructBinaryTree(preRight, inRight);
            return node;
        }
    }
    

    当然了,最好的做法是不需要构造数组,直接在原数组上进行操作,为此我们需要一个指针来做定位

    public class Solution {
        
        public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
            TreeNode root = reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
            return root;
        }
        
        public TreeNode reConstructBinaryTree(int [] pre, int preStart, int preEnd, int [] in, int inStart, int inEnd) {
            if(preStart > preEnd || inStart > inEnd) {
                return null;
            }
            TreeNode node = new TreeNode(pre[preStart]);
            for(int i = inStart; i <= inEnd; i++) {
                if(in[i] == node.val) {
                	// 左子树的长度为 i - inStart
                	// 所以新的 preEnd = preStart + 左子树的长度 = preStart + i - inStart
                    node.left = reConstructBinaryTree(pre, preStart + 1, preStart + i - inStart, in, inStart, i - 1);
                    node.right = reConstructBinaryTree(pre, preStart + i - inStart + 1, preEnd, in, i + 1, inEnd);
                    break;
                }
            }
            return node;
        }
    }
    

    小结:

    • 前序遍历得到的数组,第一个元素就是根元素
    • 中序遍历得到的数组,一旦确立根元素的位置,其左右两边分别是对应的左子树和右子树
    • 递归思想一定要熟悉掌握

  • 相关阅读:
    Java hello/hi的简单的网络聊天程序
    案例分析:设计模式与代码的结构特性
    网络相关的命令工具研究报告
    如何提高程序员的键盘使用效率
    分析一套源代码的代码规范和风格并讨论如何改进优化代码
    用例建模Use Case Modeling
    结合工程实践选题调研分析同类软件产品
    领域建模
    SecureCRT 连接虚拟机Linux
    用css固定div层在页面顶部和底部(兼容IE6)
  • 原文地址:https://www.cnblogs.com/Yee-Q/p/13660898.html
Copyright © 2011-2022 走看看