题目链接:889. 根据前序和后序遍历构造二叉树 - 力扣(LeetCode)
目录
题目描述:
返回与给定的前序和后序遍历匹配的任何二叉树。
pre
和 post
遍历中的值是不同的正整数。
方法一:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* construct(vector<int>& preorder, vector<int>& postorder, unordered_map<int, int>& postloc_map, int left, int right)
{
if (left > right) return nullptr;
//找到并建立当前区间的根节点
int root_val = preorder[left];
TreeNode* root = new TreeNode(root_val);
if (left == right) return root;
//前序遍历序列中,根节点的下一位,即为左子树的根节点
int leftroot_val = preorder[left+ 1];
//根据后续遍历序列,计算出右子树和右子树各自的长度,即结点个数
int rightlen = postloc_map[root_val] - postloc_map[leftroot_val] - 1;
int leftlen = right - left - rightlen;
root->left = construct(preorder, postorder, postloc_map, left + 1, left + leftlen);
root->right = construct(preorder, postorder, postloc_map, left + leftlen + 1, right);
return root;
}
TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
int n = preorder.size();
unordered_map<int, int> postloc_map; //记录后续遍历序列中各个元素的位置
for (int i = 0; i < n; i++)
{
postloc_map[postorder[i]] = i;
}
int left = 0; //前序遍历序列中当前区间的左指针
int right = n - 1; //前序遍历序列中当前区间的右指针
return construct(preorder, postorder, postloc_map, left, right);
}
};
只有前序遍历序列和后序遍历序列的话,不能唯一地确定一棵二叉树:只有当某个节点的度是0或者2时,这个节点的左子树和右子树才能唯一确定,否则某个子树可能是它的右子树也可能是它的左子树。所以题目中的提示说存在多种答案的话,返回一种即可。
对于前序遍历序列而言,其最左边的元素即为当前区间的根节点;对于后续遍历序列而言,其最右边的元素即为当前区间的根节点。依次特性,在前序遍历序列的当前区间中确定根节点后,其下一位就是左子树的根节点,然后在后序遍历序列中找到左子树的根节点的位置,就能够得到左子树的长度和右子树的长度。
例如: preorder:【3,9,8,5,4,10,20,15,7】
postorder: 【4,5,10,8,9,15,7,20,3】
①在前序遍历序列中,一开始的区间为left = 0, right = 8,可知当前区间的根节点为3,左子树的根节点为9
②在后续遍历序列中,找到9的位置,9和3的中间即为右子树,所以rightlen = 3,leftlen = 5。
③回到前序遍历序列中,继续递归。
方法二:
与其他两种构造二叉树的算法统一一下模板。
class Solution {
public:
//i1是当前子树的根节点在先序序列中的下标,[i2, j2]是当前子树的所有节点在后序序列中的下标
TreeNode* construct(vector<int>& preorder, vector<int>& postorder, int i1, int i2, int j2)
{
if (i2 > j2) return nullptr;
int key = preorder[i1];
TreeNode* tmp = new TreeNode(key);
//这里需要注意,当前子树只有一个节点时直接返回
//否则在下方的tmp->left的递归中,i2的值不变,k的值也不变,会继续递归下去
if (i2 == j2) return tmp; //也可以在另外两道构造二叉树的题中加上这句话,记住就行,反正不会错
int k;
//找到左子树的根节点在中序序列中的下标k,以K为边界,左边的就是左子树,右边的就是右子树
int key2 = preorder[i1 + 1]; //注意这里要找的是左子树的根节点,不是当前子树的根节点!
for (k = i2; k <= j2; k++)
{
if (postorder[k] == key2) break;
}
//i1+1是左子树的根节点在先序序列中的下标,[i2, k]是左子树的所有节点在后序序列中的下标
tmp->left = construct(preorder, postorder, i1 + 1, i2, k);
//k-i2+1是左子树的节点数量
//i1+(k-i2+1)+1就是右子树的根节点在先序序列中的下标,[k+1, j2-1]是右子树的所有节点在后序序列中的下标
tmp->right = construct(preorder, postorder, i1 + (k - i2 + 1) + 1, k + 1, j2 - 1);
return tmp;
}
TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
int n = preorder.size();
return construct(preorder, postorder, 0, 0, n - 1);
}
};
只有前序遍历序列和后序遍历序列的话,不能唯一地确定一棵二叉树:只有当某个节点的度是0或者2时,这个节点的左子树和右子树才能唯一确定,否则某个子树可能是它的右子树也可能是它的左子树。所以题目中的提示说存在多种答案的话,返回一种即可。
对于前序遍历序列而言,其最左边的元素即为当前区间的根节点;对于后序遍历序列而言,其最右边的元素即为当前区间的根节点。依此特性,在前序遍历序列的当前区间中确定根节点后,其下一位就是左子树的根节点(只要符合一种情况就行,不必纠结这棵树是否有左子树),然后在后序遍历序列中找到左子树的根节点的位置,就能够得到左子树的长度和右子树的长度。
例如: preorder:【3,9,8,5,4,10,20,15,7】
postorder: 【4,5,10,8,9,15,7,20,3】
①在前序遍历序列中,i1 = 0,可知当前子树的根节点为3,左子树的根节点为9
②在后序遍历序列中,i2 = 0,j2 = 8,找到左子树的根节点9的位置k,k和j2的中间即为右子树的节点,所以右子树的节点个数为3;i2和k的中间即为左子树的节点,所以左子树的节点个数为5。
③继续递归。