其实这两道题目就是从二叉树的前序遍历(后序遍历)和中序遍历数组中恢复出二叉数即可。我们知道二叉树的三种遍历方法后本题就变得很简单了。
先序遍历:
若二叉树为空,则不进行任何操作:否则
1、访问根结点。
2、先序方式遍历左子树。
3、先序遍历右子树。
中序遍历:
若二叉树为空,则不进行任何操作:否则
1、中序遍历左子树。
2、访问根结点。
3、中序遍历右子树。
后序遍历:
若二叉树为空,则不进行任何操作:否则
1、后序遍历左子树。
2、后序遍历右子树。
3、放问根结点。
所以当给定中序遍历和另外一种遍历方法时,我们就可以方便的结合两种遍历结果得到原始二叉树。以106题为例,题目中给出了中序遍历和后序遍历,我们首先根据后序遍历的最后一个元素确定根节点,然后再中序遍历数组中查找该元素得到其索引位置,就可以确定根节点的左右子树。然后递归调用即可。代码入下:
public TreeNode buildTree(int[] inorder, int[] postorder) {
return build(inorder, inorder.length-1, 0, postorder, postorder.length-1);
}
public TreeNode build(int[] inorder, int inStart, int inEnd, int[] postorder, int postStart){
if(inEnd > inStart)
return null;
TreeNode root = new TreeNode(postorder[postStart]);
if(inEnd == inStart)
return root;
int index = 0;
for(int i=inStart; i>=inEnd; i--){
if(inorder[i] == root.val){
index=i;
break;
}
}
root.right = build(inorder, inStart, index+1, postorder, postStart-1);
root.left = build(inorder, index-1, inEnd, postorder, postStart-(inStart-index)-1);
return root;
}
分析上面方法,我们发现对每次递归调用时都要查询中序遍历数组中根节点所在位置,所以我们可以使用HashMap来保存中序遍历数组,这样就可以节省很多中间的查询时间。但是结果却发现,使用hashMap之后代码效率反而下降了,猜测原因可能是测试用例较小,不能体现出map的优势。代码入下:
public TreeNode buildTree1(int[] inorder, int[] postorder) {
if (inorder == null || postorder == null || inorder.length != postorder.length)
return null;
HashMap<Integer, Integer> hm = new HashMap<Integer,Integer>();
for (int i=0;i<inorder.length;++i)
hm.put(inorder[i], i);
return buildTreePostIn(inorder, 0, inorder.length-1, postorder, 0,
postorder.length-1,hm);
}
private TreeNode buildTreePostIn(int[] inorder, int is, int ie, int[] postorder, int ps, int pe,
HashMap<Integer,Integer> hm){
if (ps>pe || is>ie) return null;
TreeNode root = new TreeNode(postorder[pe]);
int ri = hm.get(postorder[pe]);
root.left = buildTreePostIn(inorder, is, ri-1, postorder, ps, ps+ri-is-1, hm);
root.right = buildTreePostIn(inorder,ri+1, ie, postorder, ps+ri-is, pe-1, hm);
return root;
}
此外还可以尝试使用循环的方式来构建二叉树,代码入下:
public TreeNode buildTree(int[] inorder, int[] postorder) {
if (inorder.length == 0 || postorder.length == 0) return null;
int ip = inorder.length - 1;
int pp = postorder.length - 1;
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode prev = null;
TreeNode root = new TreeNode(postorder[pp]);
stack.push(root);
pp--;
while (pp >= 0) {
while (!stack.isEmpty() && stack.peek().val == inorder[ip]) {
prev = stack.pop();
ip--;
}
TreeNode newNode = new TreeNode(postorder[pp]);
if (prev != null) {
prev.left = newNode;
} else if (!stack.isEmpty()) {
TreeNode currTop = stack.peek();
currTop.right = newNode;
}
stack.push(newNode);
prev = null;
pp--;
}
return root;
}
那么105题就变得简单了,我们根据前序遍历和中序遍历得到原始二叉树,思路跟上面是一样的。这里就给出效率最高的方法,可以击败99。5%的用户。代码入下:
public TreeNode buildTree1(int[] preorder, int[] inorder) {
return buildTree(preorder, 0, inorder, inorder.length - 1, 0);
}
private TreeNode buildTree(int[] preorder, int idx, int[] inorder, int end, int start) {
if (idx >= preorder.length || start > end) {
return null;
}
TreeNode root = new TreeNode(preorder[idx]);
int i;
for (i = end; i >= start; i--) {
if (preorder[idx] == inorder[i]) {
break;
}
}
root.left = buildTree(preorder, idx + 1, inorder, i - 1, start);
root.right = buildTree(preorder, idx + i - start + 1, inorder, end, i+1);
return root;
}