leetcode[105] Construct Binary Tree from Inorder and Postorder Traversal
代码实现:给定一个中序遍历和后序遍历怎么构造出这颗树!(假定树中没有重复的数字)
因为没有规定是左小右大的树,所以我们随意画一颗数,来进行判断应该是满足题意的。
3 / \
2 4
/\ / \
1 6 5 7
中序遍历:1263547.
后序遍历:1625743.
我们知道后序遍历的最后一个肯定就是根了。然后在前序遍历中找到这个根,左边的就是左子树(记作subv11),右边的就是右子树(记作subv12)。在后序遍历中,前面的几个对应左子树的后序遍历(记作subv21),接下去的几个对应右子树的后序遍历(记作subv22),注意,右子树的后序遍历系系数因为3的不同位置所以要减一。
这样我们就可以分成两组的前序遍历和后序遍历的子数组了。然后递归调用返回,一个给根的左,一个给根的右。则有如下代码:
/** * Definition for binary tree * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) { if (inorder.size() == 0) return NULL; int rootval = postorder.back(); TreeNode *root = new TreeNode(rootval); vector<int> subv11, subv12, subv21, subv22; bool flag = true; for (int i = 0; i < inorder.size(); ++i) { if (inorder[i] == rootval) flag = false; if (flag && inorder[i] != rootval) { subv11.push_back(inorder[i]); subv21.push_back(postorder[i]); } else if (!flag && inorder[i] != rootval) { subv12.push_back(inorder[i]); subv22.push_back(postorder[i - 1]); } } root -> left = buildTree(subv11, subv21); root -> right = buildTree(subv12, subv22); return root; } };
如上,我是没次迭代的时候将数组分割成两部分,然后再对两部分分别递归求解。理论上应该是正确的。但是估计每次开辟新的vector存子数组用的空间一次一次递归用的空间太大了。以至于Memory Limit Exceeded
由上面的提示,我们知道,要达到好的效果那就不能一直开辟新的vector了,应该在原来的数组里操作,那么要达到和开辟新数组一样的效果就要用同时传入一些下标了。
中序遍历:1263547.
后序遍历:1625743.
同样的道理,首先我们要根据后序遍历的最后一个在中序遍历中分左右两部分,然后再确定后序遍历中相应部分的下标的起始位置。对每个数组我们传入两个下标参数,开始start和结束end下标。其中inorder的start记作is,end记作ie,postorder的start记作ps,end记作pe。
初始,中序遍历的开始为0,结束为inorder.size()-1.同理可以后序的也是类似。
每一次分割,中序遍历的126的开始为前一次开始is,结束为根节点3标号rootIndex-1,相应的后序遍历开始为前一次开始ps,因为长度要喝中序相同,所以结束为ps+(rootIndex-1-is)。
中序遍历的547的开始为根节点3标号rootIndex+1,结束为前一次的结束ie。我们可以确定结束为pe-1,因为长度相同,所以开始为pe-1-(ie-(rootIndex+1)).
那么就有代码:
/** * Definition for binary tree * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: TreeNode *fun105(vector<int> &inorder, int is, int ie, vector<int> &postorder, int ps, int pe) { if (is > ie || ps > pe || ie - is != pe - ps) return NULL; TreeNode *root = new TreeNode(postorder[pe]); int rootIndex, rootval = root -> val; for (int i = 0; i <= ie; ++i) { if (inorder[i] == rootval) rootIndex = i; } root -> left = fun105(inorder, is, rootIndex - 1, postorder, ps, ps + (rootIndex-1-is)); root -> right = fun105(inorder, rootIndex + 1, ie, postorder, pe - 1 - (ie-(rootIndex+1)), pe - 1); return root; } TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) { if (inorder.size() == 0) return NULL; return fun105(inorder, 0, inorder.size() - 1, postorder, 0, postorder.size() - 1); } };
主要的难点就是下标的控制,特别是利用长度来控制后序遍历的结束下标和起始下标。
错误的尝试:本来没有用已知长度去求后序遍历的下标,而是直接根据rootInex的下标去找后序遍历的起始,后面发现因为rootIndex在第二次迭代的时候前面已经有一个之前的根了,所以和后序遍历的下标对应就会差1,所以是错误的。迫不得已,就用已知的长度相等和已知后序遍历的某些确定下标去求未知下标了。