题目:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
这道题可以说是非常经典了,去年做实习笔试题的时候不止一次见到过这样的选择题(嗯对就算我几乎没怎么做过笔试但还是觉得很常见,而且还是放在选择题里考的),当时都是靠代入法求解,现在终于知道了正确的解题姿势(我好菜啊)。
首先考虑的是,前序遍历中,第一个出现的数字即为树的根节点,然后再去中序遍历里找,排在这个根节点前面的就都是这个根的左子树节点啦,于是我们就知道了这个根节点有多少个左子树节点,这样又可以应用到前序遍历中,因为前序遍历在遍历根以后是遍历的左子树,于是我们就知道了左子树的范围,同时,在前序遍历中,左子树序列的第一个数字也就是这个左子树的根,也就是整棵树的根节点的左子节点;如果中序遍历中这个节点之前没有其他节点了,那么就说明它没有左子节点了。右子树同理,在前序和中序遍历中排除了根节点和左子树以后,后面剩下的就是右子树了,同样的就算是右子树,在前序遍历中,根节点也是排在最前面的,于是就可以去中序遍历中找对应的根元素,它的前面就是左子树,后面就是右子树了。
思路出来了觉得真巧妙,写起代码来还是啥都不会orz 参考了讨论版里大佬们的代码才写得出来递归,代码基础可以说是非常差了。整理一下写代码的思路吧:
首先,递归基当然就是当 vector为空的时候,也就是说这个节点已经没有左右子树了,返回一个NULL作为子节点;然后通过前序遍历得到根节点的数字,并在中序遍历中找到它的位置,这样就可以确定它的左子树有多少个节点,把这个数字保存起来。接下来,就需要用到这个数字,在前序和中序序列中把当前根节点的左子树和右子树的前序和中序序列找出来,然后递归调用这个函数,求出当前根节点的左右子节点。
代码如下:
/**
* 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* reConstructBinaryTree(vector<int> pre, vector<int> vin) {
if (pre.size() == 0 || vin.size() == 0) {
return NULL;
}
TreeNode* root = new TreeNode(pre[0]);
// 求出当前的根在中序遍历中的位置
int root_pos = 0;
for (int i = 0; i < vin.size(); i++) {
if (vin[i] == root->val) {
root_pos = i;
break;
}
}
// 左子树的前序、中序序列
vector<int> left_pre, left_vin;
for (int i = 0; i < root_pos; i++) {
left_pre.push_back(pre[i + 1]);
left_vin.push_back(vin[i]);
}
// 右子树的前序、中序序列
vector<int> right_pre, right_vin;
for (int i = root_pos + 1; i < pre.size(); i++) {
right_pre.push_back(pre[i]);
right_vin.push_back(vin[i]);
}
// 递归
root->left = reConstructBinaryTree(left_pre, left_vin);
root->right = reConstructBinaryTree(right_pre, right_vin);
return root;
}
};