【力扣】根据二叉树的前序和中序遍历结果还原该二叉树(以及后序和中序还原)

简介: 【力扣】根据二叉树的前序和中序遍历结果还原该二叉树(以及后序和中序还原)

一 前序和中序还原二叉树


连接:根据二叉树的前序和中序遍历结果还原该二叉树


1f5df6e36027435fa6bb08fa4d7950ca.png


思路是这样的:


这个算法的目的是根据前序遍历和中序遍历的结果,重建一棵二叉树。

前序遍历的特点是,第一个元素一定是根节点,后面的元素是左子树和右子树的前序遍历。

中序遍历的特点是,根节点在左子树和右子树的中间,左边的元素是左子树的中序遍历,右边的元素是右子树的中序遍历。

因此,我们可以利用前序遍历确定根节点,然后在中序遍历中找到根节点的位置,从而分割出左子树和右子树的区间。

然后,我们递归地对左子树和右子树进行同样的操作,直到区间为空或者只有一个元素为止。

最后,我们返回重建好的二叉树的根节点。


代码并加上了注释:

class Solution { 
public: 
  //后序确定根 
  //中序分割左右区间 
  TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder, int& posti, int ibegin, int iend) { 
    if (ibegin > iend) return nullptr; //如果中序区间为空,返回空指针
    TreeNode* root = new TreeNode(postorder[posti]); //创建根节点,值为后序遍历的最后一个元素
    //分割左右子区间 
    int rooti = ibegin; //找到根节点在中序遍历中的位置
    while (rooti <= iend) { 
      if (inorder[rooti] == postorder[posti]) break; //找到了,跳出循环
      else rooti++; //没找到,继续向右移动
    } 
    --posti; //后序遍历的索引向前移动一位
    root->right = _buildTree(inorder, postorder, posti, rooti+1, iend); //递归构建右子树,中序区间为[rooti+1, iend]
    root->left = _buildTree(inorder, postorder, posti, ibegin, rooti-1); //递归构建左子树,中序区间为[ibegin, rooti-1]
    return root; //返回根节点
  } 
  TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) { 
    int i = postorder.size()-1; //初始化后序遍历的索引为最后一位
    return _buildTree(inorder, postorder, i, 0, inorder.size()-1); //调用辅助函数,中序区间为[0, inorder.size()-1]
  } 
};

二 后序和中序还原二叉树


如果是后序和中序让你来还原二叉树呢?

链接:后序和中序还原二叉树


还是上面那种思路,只不过这次要先递归构建右子树,这是因为后序遍历的特点是,最后一个元素是根节点,前面的元素是左子树和右子树的后序遍历,而且右子树在左子树的后面。所以,我们要先递归构建右子树,然后再构建左子树,这样才能保证后序遍历的索引正确。

59b611d4f5fe40a1b1af57d4961db3d4.png

后序遍历的最后一个元素是二叉树的根节点。

在中序遍历中找到根节点的位置,根节点左边的序列是左子树的中序遍历,右边的序列是右子树的中序遍历。

在后序遍历中找到对应的左子树和右子树的序列,左子树的长度和中序遍历中左子树的长度相同,右子树也一样。

递归地对左子树和右子树重复上述步骤,直到后序遍历或中序遍历为空。

例如,给定后序遍历 [9, 15, 7, 20, 3] 和中序遍历 [9, 3, 15, 20, 7],还原二叉树的过程如下:


后序遍历的最后一个元素是 3,它是二叉树的根节点。

在中序遍历中找到 3 的位置,它左边的序列 [9] 是左子树的中序遍历,右边的序列 [15, 20, 7] 是右子树的中序遍历。

在后序遍历中找到对应的左子树和右子树的序列,左子树只有一个元素 [9],右子树有三个元素 [15, 7, 20]。

对左子树递归地还原二叉树,由于后序遍历和中序遍历只有一个元素,所以左子节点就是 9。

对右子树递归地还原二叉树,由于后序遍历和中序遍历都不为空,重复上述步骤。


代码:

class Solution { 
public: 
  //后序确定根 
  //中序分割左右区间 
  TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder, int& posti, int ibegin, int iend) { 
    if (ibegin > iend) return nullptr; //如果中序区间为空,返回空指针
    TreeNode* root = new TreeNode(postorder[posti]); //创建根节点,值为后序遍历的最后一个元素
    //分割左右子区间 
    int rooti = ibegin; //找到根节点在中序遍历中的位置
    while (rooti <= iend) { 
      if (inorder[rooti] == postorder[posti]) break; //找到了,跳出循环
      else rooti++; //没找到,继续向右移动
    } 
    --posti; //后序遍历的索引向前移动一位
    root->right = _buildTree(inorder, postorder, posti, rooti+1, iend); //递归构建右子树,中序区间为[rooti+1, iend]
    root->left = _buildTree(inorder, postorder, posti, ibegin, rooti-1); //递归构建左子树,中序区间为[ibegin, rooti-1]
    return root; //返回根节点
  } 
  TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) { 
    int i = postorder.size()-1; //初始化后序遍历的索引为最后一位
    return _buildTree(inorder, postorder, i, 0, inorder.size()-1); //调用辅助函数,中序区间为[0, inorder.size()-1]
  } 
};


本节完

相关文章
|
2月前
【LeetCode 31】104.二叉树的最大深度
【LeetCode 31】104.二叉树的最大深度
26 2
|
2月前
【LeetCode 29】226.反转二叉树
【LeetCode 29】226.反转二叉树
20 2
|
2月前
【LeetCode 28】102.二叉树的层序遍历
【LeetCode 28】102.二叉树的层序遍历
18 2
|
2月前
【LeetCode 43】236.二叉树的最近公共祖先
【LeetCode 43】236.二叉树的最近公共祖先
21 0
|
2月前
【LeetCode 38】617.合并二叉树
【LeetCode 38】617.合并二叉树
15 0
|
2月前
【LeetCode 37】106.从中序与后序遍历构造二叉树
【LeetCode 37】106.从中序与后序遍历构造二叉树
22 0
|
2月前
【LeetCode 34】257.二叉树的所有路径
【LeetCode 34】257.二叉树的所有路径
22 0
|
2月前
【LeetCode 32】111.二叉树的最小深度
【LeetCode 32】111.二叉树的最小深度
19 0
|
4月前
|
存储 算法
二叉树进阶-学会层序遍历助你一次刷完leetcode10道题
文章深入探讨了二叉树的层序遍历方法,并展示了如何通过队列实现层序遍历的算法逻辑,同时指出掌握层序遍历技巧可以帮助解决LeetCode上的多道相关题目。
二叉树进阶-学会层序遍历助你一次刷完leetcode10道题
|
4月前
|
算法 Java
LeetCode第94题二叉树的中序遍历
文章介绍了LeetCode第94题"二叉树的中序遍历"的解法,使用递归实现了中序遍历的过程,遵循了"左根右"的遍历顺序,并提供了清晰的Java代码实现。
LeetCode第94题二叉树的中序遍历