1 问题
根据一棵树的中序遍历与后序遍历构造二叉树。
注意: 你可以假设树中没有重复的元素。
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
2 解法
首先确定如何根据中序与后序数组构造一个唯一的二叉树:
(1)每次后序数组的最后一个元素即为根节点。
(2)根据根节点将中序数组切分成左、右两部分。
(3)根据中序数组的左、右两部分切割后序数组。
(4)此时左部分属于左子树,右部分属于右子树。
(5)新的左部分/右部分的后续数组中最后一个元素为根节点。
(6)重复上述步骤。
/**
* 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* traversal(vector<int>& inorder, vector<int>& postorder)
{
if(postorder.size() == 0) return NULL;
//1.后序数组的最后一个元素即为根节点
int rootVal = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootVal);
// 递归结束条件,后序数组中仅剩一个元素,则该节点为叶子节点,遍历完成
if(postorder.size() == 1)
return root;
//2.根据根节点值,切割中序数组
//2.1 找到根节点在中序数组中的索引
int delimiterIndex; //中序数组的分割点索引
for(delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++)
{
if(inorder[delimiterIndex] == rootVal)
break;
}
//2.2 切割中序数组,区间统一左闭右开
//左中序[0, delimiterIndex)
vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
//右中序[delimiterIndex + 1, end), 加1是为了去除根节点
vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());
//3. 切割后序数组,使用左中序数组大小作为切割点
//去除根节点
postorder.resize(postorder.size() - 1);
//左后序[0, leftInorder.size())
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
//右后序[leftInorder.size(), end)
vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
//4.递归
//左中序与左后序
root->left = traversal(leftInorder, leftPostorder);
//右中序与右后序
root->right = traversal(rightInorder, rightPostorder);
//返回根节点
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.size() == 0 || postorder.size() == 0)
return NULL;
return traversal(inorder, postorder);
}
};
由于上述代码定义了多个vector,造成了资源开销过大,可以采用改变索引的方式,在同一个数组进行切片
/**
* 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* traversal(vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd)
{
//后序数组区间[postorderBegin,postorderEnd)
if(postorderBegin == postorderEnd)
return nullptr;
//1.找分割点,后序数组最后一个元素为根节点(分割点)
int rootVal = postorder[postorderEnd - 1];
//构造根节点
TreeNode* root = new TreeNode(rootVal);
//2.根据分割点切割中序数组
//2.1.找到分割点在中序数组中的索引
int delimiterIndex;
for(delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++)
{
if(inorder[delimiterIndex] == rootVal)
break;
}
//2.2.根据分割点切割中序数组
//左中序区间[inorderBegin, delimiterIndex)
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
//右中序区间[delimiterIndex + 1, inorderEnd)
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
//3.根据左中序数组长度切分后序数组
//左后序区间[postorderBegin, postorderBegin + (delimiterIndex - inorderBegin))
int leftPostorderBegin = postorderBegin;
int leftPostorderEnd = postorderBegin + (delimiterIndex - inorderBegin);
//右后序区间[postorderBegin + (delimiterIndex - inorderBegin), postorderEnd - 1)
int rightPostorderBegin = postorderBegin + (delimiterIndex - inorderBegin);
int rightPostorderEnd = postorderEnd - 1; //去掉根节点
//4.递归
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.size() == 0 || postorder.size() == 0)
return NULL;
return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
}
};