由前/中/后遍历序列构建二叉树
基础
首先,我们需要知道前中后序三种深度优先遍历二叉树的方式的具体顺序:
- 前序:中左右
- 中序:左中右
- 后序:左右中
另外,要知道只有中序+前/后序可以唯一确定一棵二叉树,而前序+后序是不能唯一确定一棵二叉树的。
思路
在构造二叉树的题目中,我们通常是采用递归形式的分治思想来实现。在前/后序列中,我们知道其最前/最后的元素一定是根结点,我们将根结点的值取出来,并在中序序列中找到该节点的值(此类题目通常保证遍历序列中无重复元素),并记下其索引。
在中序序列中,根结点左右的序列就分别是其左右子树的中序序列,我们再递归地构造其左右子树,最终得到完整的树。
105. 从前序与中序遍历序列构造二叉树
看一下前序和中序有什么特点,前序1,2,4,7,3,5,6,8
,中序4,7,2,1,5,3,8,6
;
当前要构造的树(可能是整体的某个子树)所需的前序/中序遍历序列肯定是整体所求树的前序/中序遍历序列的某个子序列,我们用 preLeft, preRight, inLeft, inRight
分别表示该子序列在整体前序/中序序列中的起始位置。
- 前序中左起第一位
1
肯定是根结点root
,我们可以根据其值rootVal
找到中序中根结点的位置记为mid
; - 中序中根结点左边就是左子树结点,右边就是右子树结点。即
[左子树结点,根结点,右子树结点]
,我们就可以得出左子树结点个数为mid-inLeft
; - 前序中结点分布应该是:
[根结点,左子树结点,右子树结点]
; - 根据前一步确定的左子树个数,可以确定前序中左子树结点和右子树结点的范围;
- 如果我们要前序遍历生成二叉树的话,下一层递归应该是:
- 左子树:
root->left = helper(preorder, inorder, 左子树前序起始点, 左子树前序终止点, 左子树中序起始点, 左子树中序终止点);
- 右子树:
root->right = helper(preorder, inorder, 右子树前序起始点, 右子树前序终止点, 右子树中序起始点, 右子树中序终止点);
- 左子树:
- 每一层递归都要返回当前根结点
root
;
全部代码如下:
class Solution {
private:TreeNode* helper(vector<int>& preorder, vector<int>& inorder, int preLeft, int preRight, int inLeft, int inRight) {if (inLeft >= inRight) return nullptr;int rootVal = preorder[preLeft];int mid = inLeft;for (; mid<inRight; ++mid) if (inorder[mid] == rootVal) break;TreeNode* root = new TreeNode(rootVal);root->left = helper(preorder, inorder, preLeft+1, preLeft+1+mid-inLeft, inLeft, mid);root->right = helper(preorder, inorder, preLeft+1+mid-inLeft, preRight, mid+1, inRight);return root;}
public:TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {return helper(preorder, inorder, 0, preorder.size(), 0, inorder.size());}
};
106. 从中序与后序遍历序列构造二叉树
与前序中序构造二叉树类似:
class Solution {
private:TreeNode* helper(vector<int>& inorder, vector<int>& postorder, int inLeft, int inRight, int postLeft, int postRight) {if (inLeft >= inRight) return nullptr;int rootVal = postorder[postRight-1];int mid = inLeft;for (; mid<inRight; ++mid) if (inorder[mid] == rootVal) break;TreeNode* root = new TreeNode(rootVal);root->left = helper(inorder, postorder, inLeft, mid, postLeft, postLeft+mid-inLeft);root->right = helper(inorder, postorder, mid+1, inRight, postLeft+mid-inLeft, postRight-1);return root;}
public:TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {return helper(inorder, postorder, 0, inorder.size(), 0, postorder.size());}
};
889. 根据前序和后序遍历构造二叉树
注意,我们之前提到前序/后序序列并不能唯一确定一棵二叉树(实际上,只有每个节点度为2或者0的时候前序和后序才能唯一确定一颗二叉树),所以本题题目说明中有:
- 每个输入保证至少有一个答案。如果有多个答案,可以返回其中一个。
class Solution {
private:TreeNode* helper(vector<int>& preorder, vector<int>& postorder, int preLeft, int preRight, int postLeft, int postRight) {if (preLeft > preRight) return nullptr;int rootVal = preorder[preLeft];TreeNode* root = new TreeNode(rootVal);if (preLeft == preRight) return root;int mid = 0;for (; mid<postRight; ++mid) if (postorder[mid] == preorder[preLeft+1]) break;root->left = helper(preorder, postorder, preLeft+1, preLeft+1+mid-postLeft, postLeft, mid);root->right = helper(preorder, postorder, preLeft+2+mid-postLeft, preRight, mid+1, postRight-1);return root;}
public:TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {return helper(preorder, postorder, 0, preorder.size()-1, 0, postorder.size()-1);}
};