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

一 前序和中序还原二叉树

连接:根据二叉树的前序和中序遍历结果还原该二叉树
在这里插入图片描述

思路是这样的:

  • 这个算法的目的是根据前序遍历和中序遍历的结果,重建一棵二叉树。
  • 前序遍历的特点是,第一个元素一定是根节点,后面的元素是左子树和右子树的前序遍历。
  • 中序遍历的特点是,根节点在左子树和右子树的中间,左边的元素是左子树的中序遍历,右边的元素是右子树的中序遍历。
  • 因此,我们可以利用前序遍历确定根节点,然后在中序遍历中找到根节点的位置,从而分割出左子树和右子树的区间。
  • 然后,我们递归地对左子树和右子树进行同样的操作,直到区间为空或者只有一个元素为止。
  • 最后,我们返回重建好的二叉树的根节点。

代码并加上了注释:

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]
  } 
};

二 后序和中序还原二叉树

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

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

在这里插入图片描述

  • 后序遍历的最后一个元素是二叉树的根节点。
  • 在中序遍历中找到根节点的位置,根节点左边的序列是左子树的中序遍历,右边的序列是右子树的中序遍历。
  • 在后序遍历中找到对应的左子树和右子树的序列,左子树的长度和中序遍历中左子树的长度相同,右子树也一样。
  • 递归地对左子树和右子树重复上述步骤,直到后序遍历或中序遍历为空。

例如,给定后序遍历 [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]
  } 
};

本节完

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
二叉树还原可以通过先序和中序遍历序列来实现。具体步骤如下: 1. 选取先序遍历序列的第一个元素作为根节点。 2. 在中序遍历序列中找到根节点的位置,将中序遍历序列分为左子树和右子树两部分。 3. 用左子树的元素个数更新先序遍历序列中序遍历序列,分别得到左子树的先序遍历序列中序遍历序列,右子树的先序遍历序列中序遍历序列。 4. 分别对左子树和右子树递归执行1-3步骤,得到左子树和右子树的二叉树。 5. 将根节点与左右子树连接起来,得到整棵二叉树。 下面是一个C++的实现代码示例: ```cpp #include<iostream> #include<vector> using namespace std; struct TreeNode{ int val; TreeNode* left; TreeNode* right; TreeNode(int x):val(x),left(nullptr),right(nullptr){} }; TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { if(preorder.empty() || inorder.empty() || preorder.size()!=inorder.size()) return nullptr; int n = preorder.size(); return build(preorder, inorder, 0, n-1, 0, n-1); } TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir){ if(pl>pr || il>ir) return nullptr; TreeNode* root = new TreeNode(preorder[pl]); int k; for(k=il;k<=ir;k++){ if(inorder[k]==preorder[pl]) break; } int leftcnt = k-il; root->left = build(preorder, inorder, pl+1, pl+leftcnt, il, k-1); root->right = build(preorder, inorder, pl+leftcnt+1, pr, k+1, ir); return root; } void preorderTraversal(TreeNode* root){ if(!root) return; cout<<root->val<<" "; preorderTraversal(root->left); preorderTraversal(root->right); } void inorderTraversal(TreeNode* root){ if(!root) return; inorderTraversal(root->left); cout<<root->val<<" "; inorderTraversal(root->right); } int main(){ vector<int> preorder = {3,9,20,15,7}; vector<int> inorder = {9,3,15,20,7}; TreeNode* root = buildTree(preorder, inorder); cout<<"preorder: "; preorderTraversal(root); cout<<endl; cout<<"inorder: "; inorderTraversal(root); cout<<endl; return 0; } ``` 运行结果: ``` preorder: 3 9 20 15 7 inorder: 9 3 15 20 7 ``` 可以看到,根据先序遍历序列中序遍历序列,成功还原二叉树

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吉始

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值