题目
递归
观察发现前序序列的首位为根,然后需要从中序序列中找到该点,这样就得到了两个前序和中序的子序列,递归即可求解。
class Solution {
public:
TreeNode* Recursion(vector<int>& preorder, vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
// 结束条件
if(preorder_left > preorder_right)
return nullptr;
// 找到前序序列的根
int preorder_root = preorder_left;
// 找到中序序列的根
int inorder_root = index[preorder[preorder_root]];
// 构造当前序列的根节点
TreeNode *root = new TreeNode(preorder[preorder_root]);
// 计算一下左子树的大小
int left_subtree_size = inorder_root - inorder_left;
// 构造左子树
root->left = Recursion(preorder, inorder, preorder_left + 1, preorder_left + left_subtree_size, inorder_left, inorder_root - 1);
// 构造右子树
root->right = Recursion(preorder, inorder, preorder_left + left_subtree_size + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
// 构造hash快速寻找中序序列的根
for(int i = 0; i < n; ++i)
index[inorder[i]] = i;
// 递归
return Recursion(preorder, inorder, 0, n - 1, 0, n - 1);
}
private:
unordered_map<int, int> index;
};
迭代
迭代的思想很有趣,观察先序序列,可以发现相邻两元素的关系只有两种情况:
- 后者是前者的左孩子;
- 后者是前者某个祖先的右孩子;
再根据中序序列的特点,可以发现一棵树的最左结点必然是中序序列的最左节点,那么就可以不断对树构造左孩子来寻找中序序列的最左结点,来构造树。
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
// 特判空树
if(!preorder.size())
return nullptr;
// 构造根
TreeNode *root = new TreeNode(preorder[0]);
// 用栈来存储所有根(父亲)
stack<TreeNode*> stk;
stk.push(root);
// 顺序遍历中序序列
int inorderIndex = 0;
for(int i = 1; i < preorder.size(); ++i) {
// 判断当前节点是否与中序序列的当前值相同
TreeNode *node = stk.top();
// 若不同则表示该节点仍有左孩子
if(node->val != inorder[inorderIndex]) {
node->left = new TreeNode(preorder[i]);
stk.push(node->left);
}
// 若相同表示该节点再无左孩子,此时开始寻找祖先的右孩子
else {
// 将祖先中无右孩子的点出栈
while(!stk.empty() && stk.top()->val == inorder[inorderIndex]) {
node = stk.top();
stk.pop();
inorderIndex++;
}
// 把右孩子给当前的根
node->right = new TreeNode(preorder[i]);
stk.push(node->right);
}
}
return root;
}
};