下面要讲的两道题,从二叉树的角度来讲,是非常重要的,此前一直是遍历二叉树,现在就要根据数组,构造二叉树
106 从中序与后序遍历序列构造二叉树 medium
示例:中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9,15,7,20,3] 返回如下的二叉树:
构造思路也不复杂,根据后序的最后一个结点(必是中间节点)为切割点,切割中序数组,再反过来切割后序数组,使用上述例子来说明该过程,见下图:
因为后序的遍历顺序为左右中,所以最后一个结点必然是根节点3
,然后根据根节点切割中序序列为根结点左子树9
和根结点右子树15 20 7
左子树就一个结点,后序的最后一个结点就是它本身,左子树至此构造完毕
右子树的最后一个结点为20
,将中序序列分割为左子树15
和右子树7
使用递归来一层一层切割是比较容易想到的。
递归函数的返回值应当是结点,因为最终要返回二叉树的根节点
传入参数则是中序和后序的序列,或者是索引值,随想录中给出了两个版本的代码
终止条件也很简单,当数组大小为零时,说明没有结点可以用于构造
若不为零,就取后序数组的最后一个元素作为根结点,以此作为中序序列的切割点(从中序序列中找到相同的值就行),将中序序列风格为左子树和右子树,以此将后序数组分割为左右子树(左右子树的内容都一样,只是索引值不同)
然后按照这个顺序,一步一步递归,构造出目标二叉树
其中最麻烦的一步应该是切割后序数组,方法是从后序的剩余数组中,按照中序左右子树的大小来切割就行
下面给出传入参数为数组的代码:
TreeNode* reversal(vector<int>& inorder, vector<int>& postorder) {
if (postorder.size() == 0) return nullptr;
int rootValue = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorder.size() == 1) return root;
// 找到中序序列的切割点
int cutPoint;
for (cutPoint = 0; cutPoint < inorder.size(); cutPoint++) {
if (inorder[cutPoint] == rootValue) break;
}
// 切割中序序列
vector<int> leftInOrder(inorder.begin(), inorder.begin() + cutPoint);
vector<int> rightInOrder(inorder.begin() + cutPoint + 1, inorder.end());
postorder.resize(postorder.size() - 1);
// 切割后序序列
vector<int> leftPostOrder(postorder.begin(), postorder.begin() + leftInOrder.size());
vector<int> rightPostOrder(postorder.begin() + leftInOrder.size(), postorder.end());
root->left = reversal(leftInOrder, leftPostOrder);
root->right = reversal(rightInOrder, rightPostOrder);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return nullptr;
return reversal(inorder, postorder);
}
借用随想录中的说法:
以上版本的代码,性能并非很好,因为每一次递归都要构造vector,耗时又耗空间,但确实最容易理解的
接下来就是参入参数为数组索引版的代码:
// 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd)
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) {
if (postorderBegin == postorderEnd) return NULL;
int rootValue = postorder[postorderEnd - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorderEnd - postorderBegin == 1) return root;
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
// 切割中序数组
// 左中序区间,左闭右开[leftInorderBegin, leftInorderEnd)
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
// 右中序区间,左闭右开[rightInorderBegin, rightInorderEnd)
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
// 切割后序数组
// 左后序区间,左闭右开[leftPostorderBegin, leftPostorderEnd)
int leftPostorderBegin = postorderBegin;
int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin; // 终止位置是 需要加上 中序区间的大小size
// 右后序区间,左闭右开[rightPostorderBegin, rightPostorderEnd)
int rightPostorderBegin = postorderBegin + (delimiterIndex - inorderBegin);
int rightPostorderEnd = postorderEnd - 1; // 排除最后一个元素,已经作为节点了
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());
}
105 从前序与中序遍历序列构造二叉树 medium
思想与上一题近似,给出示例:
前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树:
区别就在于,我们此前使用后序的最后一个结点当做中序的切割点
这道题我们直接用数组索引版本
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd) {
if (preorderBegin == preorderEnd) return NULL;
int rootValue = preorder[preorderBegin]; // 注意用preorderBegin 不要用0
TreeNode* root = new TreeNode(rootValue);
if (preorderEnd - preorderBegin == 1) return root;
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
// 切割中序数组
// 中序左区间,左闭右开[leftInorderBegin, leftInorderEnd)
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
// 中序右区间,左闭右开[rightInorderBegin, rightInorderEnd)
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
// 切割前序数组
// 前序左区间,左闭右开[leftPreorderBegin, leftPreorderEnd)
int leftPreorderBegin = preorderBegin + 1;
int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin; // 终止位置是起始位置加上中序左区间的大小size
// 前序右区间, 左闭右开[rightPreorderBegin, rightPreorderEnd)
int rightPreorderBegin = preorderBegin + 1 + (delimiterIndex - inorderBegin);
int rightPreorderEnd = preorderEnd;
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, preorder, leftPreorderBegin, leftPreorderEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, preorder, rightPreorderBegin, rightPreorderEnd);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (inorder.size() == 0 || preorder.size() == 0) return NULL;
// 参数坚持左闭右开的原则
return traversal(inorder, 0, inorder.size(), preorder, 0, preorder.size());
}