根据前序遍历和中序遍历 / 后序遍历和中序遍历重建二叉树

二叉树的遍历

在这里插入图片描述
我们知道知道
前序遍历 + 中序遍历 --------------- 可以确定唯一一棵二叉树
后序遍历 + 中序遍历 --------------- 可以确定唯一一颗二叉树
前序遍历 + 后序遍历 --------------- 不能唯一确定

!因为先序后序遍历都是确定根的位置,但不能确定左右子树的位置,一颗二叉树的建立需要根的位置也需要左右子树的位置。

练习链接:
从前序与中序遍历构造二叉树
从中序与后序遍历构造二叉树

根据前序遍历和中序遍历重建二叉树

首先来看下这两种遍历的方式, 前序遍历的遍历是:根节点 – 左子树 – 右子树 , 中序遍历是 左子树 – 根节点 – 右子树. 故前序遍历的第一个节点必然是二叉树的根节点, 来看上面二叉树的前序遍历 :
a - b - d - e - c - f - g; 故可以确定a就是二叉树的根节点。 然后我们在中序遍历中找到a这个节点。
d - b - e - (a) - f - c - g; 根据中序遍历先遍历左子树,再遍历根节点最后遍历右子树的这个特点,我们就知道了中序遍历中a的左边 d b e 是a的左子树部分, 因为中序遍历必定先遍历完根节点的左子树部分再遍历根节点, 故a的右边部分 f c g 就是a的右子树部分。 故接下来的过程就是找到a的左子树节点和a的右子树节点, 故a的左子树节点必然在中序遍历的 a 左边的部分, a 的右子树节点必定在中序遍历的a的右半部分, 以找a左子树节点为例子,此时我们知道了其左子树就是 d - b - e, 这三个节点, 而再看前序遍历, 前序遍历要先遍历根节点再遍历左子树, 故前序遍历中a后面的三个节点即为左子树的三个节点,其第一个节点即为左子树根节点。故b为a的左子树节点,同样我们以b为根节点的子树在中序遍历找到b的位置, 其位置为 d - (b) - e - a - f - c - g; 其左边为只有一个d为其左子树,右边只有一个e为其右子树。 同样找a右子树的时候, 由中序遍历知道了 f - c - g 是 a 的右子树, 故在前序遍历中知道了第一个是根节点a, 又知道其先遍历了3个左子树节点,故第五个节点开始遍历右子树,其第五个节点就是右子树的根节点。 以此递归,看下面的代码实现

/*
这里并没有专门写一个类或者结构体,表示一个节点(包括存放的数据和左右子树的指针)
直接用map表示每一个节点的左子树是那个节点和右子树是那个节点. 
*/
#include<iostream>
#include<queue>
#include<unordered_map>
using namespace std;
constexpr int N = 40;
int pre[N], in[N];
unordered_map<int, int> l, r, pos;

int Recstr_Tree(int il, int ir, int pl, int pr)
{
    int root = pre[pl];            // 可知前序遍历最先遍历根节点
    int k = pos[root];             // pos用 map快速找到根节点在中序遍历的位置 
    
    // 若il < k,即中序遍历的左边界小于当前根节点在中序遍历的节点位置,说明该根节点存在左子树, 进行递归
    // 故此时递归时, 左边子树的中序范围为 il ~ k - 1, 前序遍历的左边界要向前移动故 pl + 1, 然后前序的
    // 右边界如何确定, 可得 (k - 1) - il = x - (pl + 1)  -->  x = pl + k - il;
    if(il < k) l[root] = Recstr_Tree(il, k - 1, pl + 1, pl + k - il);   


    if(ir > k) r[root] = Recstr_Tree(k + 1, ir, pl + k - il + 1, pr);
    
    return root;          
} 

// 层序遍历 
auto level_traversal(int root)
{
    queue<int> q;
    q.push(root);
    while(q.size())
    {
        int node = q.front(); q.pop();
        cout << node << " ";
        if(l[node]) q.push(l[node]);
        if(r[node]) q.push(r[node]);
    }
}

auto main() -> int
{
    int n; cin >> n;
    for(int i = 0; i < n; ++i) cin >> pre[i];    // 输入前序遍历
    for(int i = 0; i < n; ++i)
    {
        cin >> in[i];                           // 输入中序遍历
        pos[in[i]] = i;                         // 保存每个节点在中序遍历的位置
    }
    int root = Recstr_Tree(0, n - 1, 0, n - 1); // 重建树
    level_traversal(root);                      // 进行层序遍历
    cout << endl;
    return 0;
}

根据后序遍历和中序遍历重建二叉树

相关题目的链接 : AcWing 1497. 树的遍历

同样的话根据后序遍历 和 中序遍历重建二叉树也是同理,后序遍历是先遍历左子树 – 遍历右子树 – 最后遍历根节点, 故每次确定根节点应该是用后序遍历的右边界来确定, 故代码实现基本差不多 .

#include<iostream>
#include<queue>
#include<unordered_map>
using namespace std;
constexpr int N = 40;
int post[N], in[N];
unordered_map<int, int> l, r, pos;

int Recstr_Tree(int il, int ir, int pl, int pr)
{
    int root = post[pr];
    int k = pos[root];
    if(il < k) l[root] = Recstr_Tree(il, k - 1, pl, pl + k - il - 1);
    if(ir > k) r[root] = Recstr_Tree(k + 1, ir, pl + k - il, pr - 1); 
    return root;
}

auto level_traversal(int root)
{
    queue<int> q;
    q.push(root);
    while(q.size())
    {
        int node = q.front(); q.pop();
        cout << node << " ";
        if(l[node]) q.push(l[node]);
        if(r[node]) q.push(r[node]);
    }
}

auto main() -> int
{
    int n; cin >> n;
    for(int i = 0; i < n; ++i) cin >> post[i]; 
    for(int i = 0; i < n; ++i)
    { 
        cin >> in[i]; 
        pos[in[i]] = i;
    }
    int root = Recstr_Tree(0, n-1, 0, n-1);
    level_traversal(root);
    cout << endl;
    return 0;
}

两道题目的答案

前序 + 中序

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution 
{
public:
    unordered_map<int, int> pos; 
    TreeNode* Rcstr(vector<int>& preorder, vector<int>& inorder, int il, int ir, int pl, int pr)
    {
        TreeNode *root = new TreeNode(preorder[pl]); 
        int k = pos[preorder[pl]]; 

        if(il < k) root->left = Rcstr(preorder, inorder, il, k - 1, pl + 1, pl + k - il);  
        if(k < ir) root->right = Rcstr(preorder, inorder, k + 1, ir, pl + k - il + 1 ,pr); 
        return root; 
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) 
    {
        int n = inorder.size();
        for(int i = 0; i < n; ++i) pos[inorder[i]] = i; 
        TreeNode *root = Rcstr(preorder, inorder, 0, n - 1, 0, n - 1); 
        return root;
    }
};

后序 + 中序

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution 
{
public:
    unordered_map<int, int> pos; 
    TreeNode* Rcstr(vector<int> &inorder, vector<int> &postorder, int il, int ir, int pl, int pr)
    {
        TreeNode *root = new TreeNode(postorder[pr]); 
        int k = pos[postorder[pr]]; 

        if(il < k) root->left = Rcstr(inorder, postorder, il, k - 1, pl, pl + k - il - 1); 
        if(k < ir) root->right = Rcstr(inorder, postorder, k + 1, ir, pl + k - il, pr - 1);

        return root; 
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
    {
        int n = inorder.size(); 
        for(int i = 0; i < n; ++i) pos[inorder[i]] = i; 
        TreeNode *res = Rcstr(inorder, postorder, 0, n - 1, 0, n - 1); 
        return res; 
    }
};

根据层序遍历和中序遍历重建二叉树

如何通过层序遍历序列和中序遍历序列来确定一棵二叉树?

  • 根据层序遍历序列第一个结点确定根结点;
  • 根据根结点在中序遍历序列中分割出左右子树的中序序列;
  • 根据分割出的左右子树的中序序列从层序序列中提取出对应的左右子树的层序序列;
  • 对左子树和右子树分别递归使用相同的方式继续分解;

层序遍历的一个特点就是其第一个节点表示的是根节点, 但是其本身的层序遍历的序列并没有按照一定的顺序, 如前面的前序遍历和后序遍历都是按照一定的顺序, 根 -> 左子树 -> 右子树 / 左子树 -> 右子树 -> 根。故我们可以很容易的写出其递归方程, 但是这里的层序遍历我们唯一能够确认的是其第一个必定是整棵二叉树的根节点. 所以根据这个条件我们每次遍历的时候都要重新构建其中左子树和右子树的层序序列, 以此来确定其左右子树的根节点.

具体实现

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<unordered_map>
using namespace std;  

unordered_map<int, int> pos; 

struct TreeNode
{
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

TreeNode* buildTree(vector<int>& levelorder, vector<int>& inorder) 
{
    int n = levelorder.size();
    TreeNode *root = new TreeNode(levelorder[0]);
    cout << "n = " << n << "  val = " << root->val << endl; 
    int k = pos[levelorder[0]];           // 其中 pos 为快速找到对应元素在中序遍历中的位置 
    vector<int> left_level, right_level;  // 记录左右子树的层序遍历
    
    // 重新构建左右子树的层序遍历 
    // 中序遍历确定左右子树个数 
    // 然后根据现有的层序遍历的顺序来构建左右子树的层序序列  
    
    for(int i = 0; i < n; ++i) 
    {
        for(int j = 0; j < k; ++j) 
            if(levelorder[i] == inorder[j]) left_level.push_back(inorder[j]);
        for(int c = k + 1; c < inorder.size(); ++c)
            if(levelorder[i] == inorder[c]) right_level.push_back(inorder[c]);
    }
    
    if(left_level.size())  root->left = buildTree(left_level, inorder); 
    if(right_level.size()) root->right = buildTree(right_level, inorder); 
    return root;
}
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值