2021.10.7 力扣-根据前序和后序遍历构造二叉树

题目链接:889. 根据前序和后序遍历构造二叉树 - 力扣(LeetCode)

目录

题目描述:

方法一:

方法二:


题目描述:

返回与给定的前序和后序遍历匹配的任何二叉树。

 pre 和 post 遍历中的值是不同的正整数。

方法一:

/**
 * 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:
     TreeNode* construct(vector<int>& preorder, vector<int>& postorder, unordered_map<int, int>& postloc_map, int left, int right)
     {
         if (left > right) return nullptr;
         //找到并建立当前区间的根节点
         int root_val = preorder[left];
         TreeNode* root = new TreeNode(root_val);
         if (left == right) return root;
         //前序遍历序列中,根节点的下一位,即为左子树的根节点
         int leftroot_val = preorder[left+ 1];
         //根据后续遍历序列,计算出右子树和右子树各自的长度,即结点个数
         int rightlen = postloc_map[root_val] - postloc_map[leftroot_val] - 1;
         int leftlen = right - left - rightlen;
         
         root->left = construct(preorder, postorder, postloc_map, left + 1, left + leftlen);
         root->right = construct(preorder, postorder, postloc_map, left + leftlen + 1, right);

         return root;
     }
     TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
         int n = preorder.size();
         unordered_map<int, int> postloc_map;   //记录后续遍历序列中各个元素的位置
         for (int i = 0; i < n; i++)
         {
             postloc_map[postorder[i]] = i;
         }
         int left = 0;      //前序遍历序列中当前区间的左指针
         int right = n - 1; //前序遍历序列中当前区间的右指针
         return construct(preorder, postorder, postloc_map, left, right);
     }
 };

只有前序遍历序列和后序遍历序列的话,不能唯一地确定一棵二叉树:只有当某个节点的度是0或者2时,这个节点的左子树和右子树才能唯一确定,否则某个子树可能是它的右子树也可能是它的左子树。所以题目中的提示说存在多种答案的话,返回一种即可。

对于前序遍历序列而言,其最左边的元素即为当前区间的根节点;对于后续遍历序列而言,其最右边的元素即为当前区间的根节点。依次特性,在前序遍历序列的当前区间中确定根节点后,其下一位就是左子树的根节点,然后在后序遍历序列中找到左子树的根节点的位置,就能够得到左子树的长度和右子树的长度。

例如:                        preorder:【3,9,8,5,4,10,20,15,7】
                                   postorder: 【4,5,10,8,9,15,7,20,3】

①在前序遍历序列中,一开始的区间为left = 0, right = 8,可知当前区间的根节点为3,左子树的根节点为9

②在后续遍历序列中,找到9的位置,9和3的中间即为右子树,所以rightlen = 3,leftlen = 5。

③回到前序遍历序列中,继续递归。

方法二:

与其他两种构造二叉树的算法统一一下模板。

class Solution {
public:
    //i1是当前子树的根节点在先序序列中的下标,[i2, j2]是当前子树的所有节点在后序序列中的下标
    TreeNode* construct(vector<int>& preorder, vector<int>& postorder, int i1, int i2, int j2)
    {
        if (i2 > j2) return nullptr;
        int key = preorder[i1];
        TreeNode* tmp = new TreeNode(key);
        //这里需要注意,当前子树只有一个节点时直接返回
        //否则在下方的tmp->left的递归中,i2的值不变,k的值也不变,会继续递归下去
        if (i2 == j2) return tmp;    //也可以在另外两道构造二叉树的题中加上这句话,记住就行,反正不会错
        int k;
        //找到左子树的根节点在中序序列中的下标k,以K为边界,左边的就是左子树,右边的就是右子树
        int key2 = preorder[i1 + 1];    //注意这里要找的是左子树的根节点,不是当前子树的根节点!
        for (k = i2; k <= j2; k++)
        {
            if (postorder[k] == key2) break;
        }
        //i1+1是左子树的根节点在先序序列中的下标,[i2, k]是左子树的所有节点在后序序列中的下标
        tmp->left = construct(preorder, postorder, i1 + 1, i2, k);
        //k-i2+1是左子树的节点数量
        //i1+(k-i2+1)+1就是右子树的根节点在先序序列中的下标,[k+1, j2-1]是右子树的所有节点在后序序列中的下标
        tmp->right = construct(preorder, postorder, i1 + (k - i2 + 1) + 1, k + 1, j2 - 1);
        return tmp;
    }
    TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
        int n = preorder.size();
        return construct(preorder, postorder, 0, 0, n - 1);
    }
};

只有前序遍历序列和后序遍历序列的话,不能唯一地确定一棵二叉树:只有当某个节点的度是0或者2时,这个节点的左子树和右子树才能唯一确定,否则某个子树可能是它的右子树也可能是它的左子树。所以题目中的提示说存在多种答案的话,返回一种即可。

对于前序遍历序列而言,其最左边的元素即为当前区间的根节点;对于后序遍历序列而言,其最右边的元素即为当前区间的根节点。依此特性,在前序遍历序列的当前区间中确定根节点后,其下一位就是左子树的根节点(只要符合一种情况就行,不必纠结这棵树是否有左子树),然后在后序遍历序列中找到左子树的根节点的位置,就能够得到左子树的长度和右子树的长度。

例如:                        preorder:【3,9,8,5,4,10,20,15,7】
                                   postorder: 【4,5,10,8,9,15,7,20,3】

①在前序遍历序列中,i1 = 0,可知当前子树的根节点为3,左子树的根节点为9

②在后序遍历序列中,i2 = 0,j2 = 8,找到左子树的根节点9的位置k,k和j2的中间即为右子树的节点,所以右子树的节点个数为3;i2和k的中间即为左子树的节点,所以左子树的节点个数为5。

③继续递归。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值