题目五:
解法一:(递归)
解题思路:这个题跟中序和后续创建二叉树类似,主要就是要画图,画图就十分清晰了,注意的是,最先处理的根节点,是从前序遍历中shift,即前序遍历中便没有了这个元素了,这点很重要,不然边界会出问题。
思路:
- 构建一个二叉树需要构建三部分:root、左子树、右子树
- 左子树、右子树的构建,又包括:root、左子树、右子树
- 解题关键在于定位出根节点,划分出左右子树,然后 递归 构建左右子树
具体做法:
- preorder 数组的第一项肯定是根节点 —— 因为前序遍历的顺序是 根| 左|右。
- 根据根节点,在 inorder [左 | 根 | 右] 中划分出分别属于左、右子树的 inorder 序列。
- 并求出左右子树的节点个数,在 preorder 中划分出分别属于左、右子树的 preorder 序列。
- 于是就有了左、右子树的 preorder 和 inorder 序列,递归构建左、右子树就好。
var buildTree = function(preorder, inorder) {
if (!preorder.length) return null;
const rootVal = preorder.shift(); // 从前序遍历的数组中获取中间节点的值, 即数组第一个值
const index = inorder.indexOf(rootVal); // 获取中间节点在中序遍历中的下标
const root = new TreeNode(rootVal); // 创建中间节点
root.left = buildTree(preorder.slice(0, index), inorder.slice(0, index)); // 创建左节点
root.right = buildTree(preorder.slice(index), inorder.slice(index + 1)); // 创建右节点
return root;
};
优化:字符串截取存在性能消耗,没必要每次都切割。用两个指针表示即可。递归函数传指针。
const buildTree = (preorder, inorder) => {
const helper = (p_start, p_end, i_start, i_end) => {
if (p_start > p_end) return null;
let rootVal = preorder[p_start]; // 根节点的值
let root = new TreeNode(rootVal); // 根节点
let mid = inorder.indexOf(rootVal); // 根节点在inorder的位置
let leftNum = mid - i_start; // 左子树的节点数
root.left = helper(p_start + 1, p_start + leftNum, i_start, mid - 1);
root.right = helper(p_start + leftNum + 1, p_end, mid + 1, i_end);
return root;
};
return helper(0, preorder.length - 1, 0, inorder.length - 1);
};