重建二叉树

2年前 (2022) 程序员胖胖胖虎阿
222 0 0

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列 {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;
    }
}

小结:

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

版权声明:程序员胖胖胖虎阿 发表于 2022年10月26日 上午3:40。
转载请注明:重建二叉树 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...