牛客Top101第十三天

BM40.重建二叉树

题目描述:

给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。

例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示

方法一:递归(推荐使用)

知识点:二叉树递归

递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能讲原本的问题分解为更小的子问题,这是使用递归的关键。

而二叉树的递归,则是将某个节点的左子树、右子树看成一颗完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数不断进入子树。

思路:

对于二叉树的前序遍历,我们知道序列的第一个元素必定是根节点的值,因为序列没有重复的元素,因此中序遍历中可以找到相同的这个元素,而我们又知道中序遍历中根节点将二叉树分成了左右子树两个部分,如下图所示:

图片说明

我们可以发现,数字1是根节点,并将二叉树分成了(247)和(3568)两棵子树,而子树的的根也是相应前序序列的首位,比如左子树的根是数字2,右子树的根是数字3,这样我们就可以利用前序遍历序列找子树的根节点,利用中序遍历序列区分每个子树的节点数。

具体做法:

  • step 1:先根据前序遍历第一个点建立根节点。
  • step 2:然后遍历中序遍历找到根节点在数组中的位置。
  • step 3:再按照子树的节点数将两个遍历的序列分割成子数组,将子数组送入函数建立子树。
  • step 4:直到子树的序列长度为0,结束递归。
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        int n = pre.size();
        int m = vin.size();
        //每个遍历都不能为0
        if(n == 0 || m == 0) 
            return NULL;
        //构建根节点
        TreeNode *root = new TreeNode(pre[0]); 
        for(int i = 0; i < vin.size(); i++){
            //找到中序遍历中的前序第一个元素
            if(pre[0] == vin[i]){ 
                //左子树的前序遍历
                vector<int> leftpre(pre.begin() + 1, pre.begin() + i + 1);  
                //左子树的中序遍历
                vector<int> leftvin(vin.begin(), vin.begin() + i); 
                //构建左子树
                root->left = reConstructBinaryTree(leftpre, leftvin); 
                //右子树的前序遍历
                vector<int> rightpre(pre.begin() + i + 1, pre.end()); 
                //右子树的中序遍历
                vector<int> rightvin(vin.begin() + i + 1, vin.end()); 
                //构建右子树
                root->right = reConstructBinaryTree(rightpre, rightvin); 
                break;
            }
        }
        return root;
    }
};

代码随想录做法:

  • 第一步:如果数组大小为零的话,说明是空节点了。

  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。

  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点

  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)

  • 第五步:切割后序数组,切成后序左数组和后序右数组

  • 第六步:递归处理左区间和右区间

class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int>& preOrder, vector<int>& vinOrder) {
        if(preOrder.size() == 0) return nullptr;

        int rootValue = preOrder[0];
        TreeNode* root = new TreeNode(rootValue);

        if(preOrder.size() == 1) return root;

        int deli;
        for(deli = 0; deli < vinOrder.size(); deli++){
            if(rootValue == vinOrder[deli])
            break;
        }

        //切中序遍历
        vector<int> leftOrder(vinOrder.begin(), vinOrder.begin() + deli);
        vector<int> rightOrder(vinOrder.begin() + deli + 1, vinOrder.end());
        
        //切前序遍历
        vector<int>leftPre(preOrder.begin() + 1, preOrder.begin() + leftOrder.size() + 1);
        vector<int>rightPre(preOrder.begin() + leftOrder.size() + 1, preOrder.end());

        root->left = reConstructBinaryTree(leftPre, leftOrder);
        root->right = reConstructBinaryTree(rightPre, rightOrder);

        return root;
    }
};

方法二:栈(思路扩展)

知识点:栈 栈是一种仅支持在表尾进行插入和删除操作的线性表,这一端被称为栈顶,另一端被称为栈底。元素入栈指的是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;元素出栈指的是从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

思路:

除了递归,我们也可以用类似非递归前序遍历的方式建立二叉树,利用栈辅助进行非递归,然后依次建立节点。

具体做法:

  • step 1:首先前序遍历第一个数字依然是根节点,并建立栈辅助遍历。
  • step 2:然后我们就开始判断,在前序遍历中相邻的两个数字必定是只有两种情况:要么前序后一个是前一个的左节点;要么前序后一个是前一个的右节点或者其祖先的右节点。
  • step 3:我们可以同时顺序遍历pre和vin两个序列,判断是否是左节点,如果是左节点则不断向左深入,用栈记录祖先,如果不是需要弹出栈回到相应的祖先,然后进入右子树,整个过程类似非递归前序遍历。
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        int n = pre.size();
        int m = vin.size();
        //每个遍历都不能为0
        if(n == 0 || m == 0) 
            return NULL;
        stack<TreeNode*> s;
        //首先建立前序第一个即根节点
        TreeNode *root = new TreeNode(pre[0]); 
        TreeNode *cur = root;
        for(int i = 1, j = 0; i < n; i++){
            //要么旁边这个是它的左节点
            if(cur->val != vin[j]){ 
                cur->left = new TreeNode(pre[i]);
                s.push(cur);
                //要么旁边这个是它的右节点,或者祖先的右节点
                cur = cur->left; 
            }else{
                j++;
                //弹出到符合的祖先
                while(!s.empty() && s.top()->val == vin[j]){ 
                    cur = s.top();
                    s.pop();
                    j++;
                }
                //添加右节点
                cur->right = new TreeNode(pre[i]); 
                cur = cur->right;
            }
        }
        return root;
    }
};

BM41.输出二叉树的右视图

题目描述

请根据二叉树的前序遍历,中序遍历恢复二叉树,并打印出二叉树的右视图

数据范围:0≤n≤10000
要求: 空间复杂度O(n),时间复杂度 O(n)

如输入[1,2,4,5,3],[4,2,5,1,3]时,通过前序遍历的结果[1,2,4,5,3]和中序遍历的结果[4,2,5,1,3]可重建出以下二叉树:所以对应的输出为[1,3,5]。

class Solution {
public:
    void writeRightView(vector<int> xianxu, vector<int> zhongxu, vector<int>& RightView, int curr_level){
        if(xianxu.empty())
            return;
        //无右子树,左子树先序[0]就是右视图
        if(RightView.size()<curr_level)
            RightView.push_back(xianxu[0]);
        //有右子树,更改为右子树先序[0]
        else
            RightView[curr_level-1]=xianxu[0];
        int head_pos=0;
        while(zhongxu[head_pos]!=xianxu[0])
            head_pos++;
        writeRightView(vector<int>(xianxu.begin()+1,xianxu.begin()+head_pos+1),vector<int>(zhongxu.begin(),zhongxu.begin()+head_pos),RightView,curr_level+1);
        writeRightView(vector<int>(xianxu.begin()+head_pos+1,xianxu.end()),vector<int>(zhongxu.begin()+head_pos+1,zhongxu.end()),RightView,curr_level+1);   //先左后右
    }

    vector<int> solve(vector<int>& xianxu, vector<int>& zhongxu) {
        vector<int> res;
        writeRightView(xianxu, zhongxu, res, 1);
        return res;
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值