代码随想录二刷 | 二叉树 | 从中序与后序遍历序列构造二叉树
- 题目描述
- 解题思路
- 代码实现
题目描述
106.从中序与后序遍历序列构造二叉树
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
示例 2:
输入:inorder = [-1], postorder = [-1]
输出:[-1]
提示:
- 1 <= inorder.length <= 3000
- postorder.length == inorder.length
- -3000 <= inorder[i], postorder[i] <= 3000
- inorder 和 postorder 都由 不同 的值组成
- postorder 中每一个值都在 inorder 中
- inorder 保证是树的中序遍历
- postorder 保证是树的后序遍历
解题思路
根据两个顺序构建二叉树,注意三个点:
- 第一,前序遍历为中左右,中序遍历为左右中,后序遍历是左右中;
- 第二,后序遍历的最后一个元素一定是根节点;
- 第三,前序数组、中序数组、后序数组的大小是相等的。
找到根节点后再根据每个遍历的特点,就能在纸上很轻松地还原出二叉树。
这里使用递归一步一步拆分:
- 第一步:如果数组大小为0,说明为空节点
if (postorder.size() == 0) return NULL;
- 第二步:如果不为空,那么取后序数组的最后一个节点为根节点
int rootValue = postorder[postorder.size() - 1]; TreeNode* root = new TreeNode(rootValue); // 如果root就是叶子节点,也就是说这是一个只有根节点的二叉树,直接返回root if (postorder.size() == 1) return root;
- 第三步:找到根节点在中序数组的位置,作为拆分点
int delimiterIndex; for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {if (inorder[delimiterIndex] == rootValue) break; }
- 第四步:拆分中序数组,左边为中序左数组,右边为中序右数组,(中序遍历为左右中)
// 左闭右开区间 [0, delimiterIndex) vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex); // [delimeterIndex, end) vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end())
- 第五步:拆分后序数组,拆成后序左数组和后序右数组(后序遍历为左右中)
// 因为后序数组没有很明显的拆分点,但它的长度是和中序数组相等的,对应的根节点的左右子树的长度也是相等的,所以可以根据中序左数组的大小作为拆分点来拆分,拆分成后序左数组和后序右数组。 // 舍弃末尾元素,因为这个元素已经是根节点了,我们拆分出来的实际是根节点的左右子树,所以先去掉它 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() + 1, postorder.end());
- 第六步,递归处理左数组和有数组
root->left = traversal(leftInorder, leftPostorder); root->left = traversal(rightInorder, rightPostorder); return root;
代码实现
class Solution {
private:TreeNode* traversal(vector<int>& inorder, vector<int>& postorder) {// 空节点,返回NULLif (postorder.size() == 0) return NULL;// 后序数组的最后一个元素为根节点int rootValue = postorder.size() - 1;TreeNode* root = new TreeNode(rootValue); // 根节点为叶子节点,返回rootif (postorder.size() == 1) return root;// 找到中序遍数组的拆分点int delimiterIndex;for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {if (inorder[delimiterIndex] == rootValue) break;}// 切割中序数组// [0, delimiterIndex)vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);// [delimiterIndex + 1, end)vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());// postorder 去掉末尾元素 postorder.resize(postorder.size() - 1);// 拆分后序数组// [0, leftInorder.size())vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());// [leftInorder,size(), end)// 没有 + 1的原因是,虽然是左闭右开,但是已经将根节点剔除了vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());// 递归左右数组root->left = traversal(leftInorder, leftPostorder);root->right = traversal(rightInorder, rightPostorder);return root;}
public:TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {if (inorder.size() == 0 || postorder.size() == 0) return NULL;return ttraversal(inorder, postorder);}
};