LeetCode_Construct Binary Tree from Inorder and Postorder Traversal

给定二叉树中序、后序遍历结果,构建二叉树。

  1. 后序的最后一个结点必为根节点
  2. 对于后序的最后一个结点而言,中序遍历结果中,位于它左边的是它的左子树,位于它右边的是它的右子树。
  3. 由 2 可得基本递归思路一:不断利用后序最后一个节点将中序结果分割,自底向上递归建立左右子树即可。此时,由于要在中序结果中寻找后序最后结点,故要么每次都循环遍历(时间代价),要么提前建立一个哈希表(空间代价)作为参数。
  4. 本文的重点是第二种递归思路,既不循环也不需要哈希表。
  5. 代码如下(转自参考资料):
    int pInorder;   // index of inorder array
    int pPostorder; // index of postorder array
    
    private TreeNode buildTree(int[] inorder, int[] postorder, TreeNode end) {
    	if (pPostorder < 0) {
    		return null;
    	}
    	
    	// create root node
    	TreeNode n = new TreeNode(postorder[pPostorder--]);
    	
    	// if right node exist, create right subtree
    	if (inorder[pInorder] != n.val) {
    		n.right = buildTree(inorder, postorder, n);
    	}
    	
    	pInorder--;
    	
    	// if left node exist, create left subtree
    	if ((end == null) || (inorder[pInorder] != end.val)) {
    		n.left = buildTree(inorder, postorder, end);
    	}
    	
    	return n;
    }
    
    public TreeNode buildTree(int[] inorder, int[] postorder) {
    	pInorder = inorder.length - 1;
    	pPostorder = postorder.length - 1;
    	
    	return buildTree(inorder, postorder, null);
    }
    
  6. 代码整体亦使用递归思路,同时还使用了两个函数外指针变量分别遍历两个结果
  7. 右子树的判断:先以后序遍历的末尾值建立根结点 n,此时判定对应位置的中序结果(inorder[pInorder])是否与后序(n.val)的一致,若不一致,说明该根结点有右子树,导致后序遍历的末尾值在中序遍历不在末尾,则进入递归函数进行右子树结点的建立,循环往复直至当前结点(n.val)处于中序末尾,也就是二叉树最右结点。此外,需要注意的是,后序指针(pPostorder )在建立完当前结点后立即前移
  8. 在递归进入并建立完最深一层的最右结点后,中序指针前移。此时需要进行左子树的判断。
  9. 左子树的判断:左子树相比于右子树要较难一些。为便于说明,称当前结点(n)右子结点的根结点为“父结点”、当前结点左子结点(n.left)为“左结点”。第8点提到了中序指针前移,若当前结点没有左结点(或左子树)时前移后的中序指针应恰好指向父结点而若有左结点(或左子树)则中序结果中左结点(或左子树)会插在父结点与当前结点之间。也就是说,父结点是左结点(或左子树)的边界(TreeNode end is the boundary of left subtree.)。那么,我们该怎么做呢?判定前移后的中序指针指向的是否为父结点。问题是,如何得知父结点具体是多少呢?
  10. 这就得介绍一下我们的递归函数的第三个形参:TreeNode end。从右子树部分的递归调用语句中可以看出,我们通过它将当前结点作为根结点传递给下一层递归,也就是说,当前层的形参TreeNode end里存放的就是我们在第9条中需要的边界信息——父结点。而在左子树部分,由于可能会进行多次递归,故要将本层接收到的形参(TreeNode end,并不是TreeNode n)作为实参继续传递给下一层(在递归传递过程中,end并不是一成不变的,当递归到某一步遇到有右子树的情况时,需要展开新的递归,此时,那一层创建的当前结点n是新展开的递归分支的end,而这个递归分支也可能展开另一个分支…但无论情况有多么复杂,当递归一层层退回的时候通过压栈入栈又可恢复原本的正确的end)。
  11. 然而,引入TreeNode end作为形参会带来一个新问题。对于一个二叉树而言,最顶结点没有父结点。那么,在一开始启动这个递归函数的时候,又该传递给它什么实参呢?答案是null,我们将最顶结点的父结点,人为定义为null,换个角度讲,最顶结点(或者说它代表的整个二叉树)被看作是null的右子结点(或右子树)。那么对应的,在判断是否有左子树的时候,就得增加一个“或”条件判断end是否为null。
  12. 另,题中给定了一个限制条件是二叉树结点无重复值,读者可思考在有重复值的情况下, 哪些步骤会失效。在这种复杂情况下,又该以何作为判定依据。

参考:

  1. 力扣对应题目讨论区的某答案下第一个评论
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值