「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
从中序与后序遍历序列构造二叉树
根据一棵树的中序遍历与后序遍历构造二叉树。
注意: 你可以假设树中没有重复的元素。
例如,给出 中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9,15,7,20,3] 返回如下的二叉树:
解法
首先中序遍历的遍历次序是左中右,后序遍历的遍历次序是左右中,根据这两个次序,我们可以发现一些规律,我们从一个普通子树画图讲解一下这些规律
这颗子树是没有重复元素的,我们能发现:
- 后序遍历数组的最后一个元素是根节点的值,我们可以根据这个构建根节点
- 中序遍历数组中,根节点前面的元素是其左子树各节点的val值(也是按照中序遍历次序排列),根节点后面的元素是其右子树各节点的val值(也是按照中序遍历次序排列),所以我们从后序遍历中得到根节点的val之后,就可以找到根节点在中序遍历数组中的位置,以此分割左子树和右子树的中序数组
- 一棵树中序遍历和后序遍历的数组长度是一样的,我们可以从分割好的左子树和右子树的中序遍历数组,得到数组长度,分割左子树和右子树的后序遍历数组
依照以上三点,我们可以利用中序遍历数组和后序遍历数组递归构建完整的二叉树,每次递归,都构建当前中序和后序数组的根节点,并返回给父节点,用以父节点的left和right构建,递归的终止条件是中序或者后序数组为空(证明已经构建到了当前路径的叶子结点)
代码如下
var buildTree = function(inorder, postorder) {
const buildSubTree = function(inorder, postorder) {
// 如果传入的中序遍历数组长度为0,证明已经递归到了叶子节点
if (inorder.length === 0) return null;
// 构建当前迭代的根节点
let rootNode = new TreeNode(postorder.pop());
// 分割根节点的中序遍历数组形成根节点左子树的中序遍历数组
let leftInorder = inorder.slice(0,inorder.indexOf(rootNode.val));
// 分割根节点的后序遍历数组形成根节点左子树的后序序遍历数组
let leftPostorder = postorder.slice(0,leftInorder.length);
// 分割根节点的中序遍历数组形成根节点右子树的中序遍历数组
let rightInorder = inorder.slice(inorder.indexOf(rootNode.val)+1);
// 分割根节点的后序遍历数组形成根节点右子树的后序序遍历数组
let rightPostorder = postorder.slice(leftInorder.length)
// 构建当前根节点的左子节点并做连接
rootNode.left = buildSubTree(leftInorder, leftPostorder);
// 构建当前根节点的右子节点并做连接
rootNode.right = buildSubTree(rightInorder, rightPostorder);
// 返回当前根节点供其父节点做节点连接
return rootNode;
}
// 如果初始的中序遍历数组长度为0,证明该树为空
if (inorder.length === 0) return null;
// 递归构建树
let root = buildSubTree(inorder, postorder);
return root;
};