每日打卡:从前序与中序遍历序列构造二叉树

363 阅读3分钟

这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]

原题链接

我只能说,误入歧途,原本想找一个简单题做一下,看着这前序和中序,还想都很熟悉的样子

其实吧,这道题,考的就是一个思维,当你通窍了,瞬间就觉得不难了,我就在看完题解之后,我还想了好长一段时间,这到底是怎么回事。

要想知道我们这道怎么做,我们首先要知道,我们的三种遍历顺序

  • 前序:根 →左 →右
  • 中序:左 →根 →右
  • 后序:左 →右 →根

我们这道题就主要用到了前两个,那我们就需要找到,我们这两种遍历,都有什么顺序,我们打眼一看,就看到,我们两种遍历的一个共同点,就是最后访问我们的右节点,访问左节点和根节点的顺序正好是反的

通过前序遍历我们可以确定我们根节点,再根据我们的中序遍历,我们可以找到他的左右子节点

A8E27CFD4AB5CF2300B6A1DDA5B00ABF.jpg

我们要注意的一点是,我们根据前序,我们就只能确定我们的根节点,根绝中序遍历的时候,我们只能确定根节点前面的一个数,是他的左节点,根节点后面的不一定是他的右节点

当我们把这个道理弄清楚之后,我们就可以开始我们代码的编写

    public TreeNode buildTree(int[] preorder, int[] inorder) {
    //首先我们需要创建哈希表来存入我们的中序遍历及位置,
    //因为我们需要根据我们的节点值,来确定我们根节点在中序遍历中的位置
    //通过根节点来分割我们的左右子树
        Map<Integer,Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i < inorder.length; i++) {
            //因为通过节点值寻找,所以我们的key为节点值
            map.put(inorder[i],i);
        }
        
        return buildTree(preorder,map,0, preorder.length-1,0,inorder.length-1 );
    }
    //我们的中序遍历直接通过map的方式传入我们的方法
    //pLeft,pRight,iLeft,iLeft,为我们两种遍历的左右边界
    //因为我们两种遍历只是根节点和左节点的位置不同
    public TreeNode buildTree(int[] preorder,Map<Integer,Integer> map,int pLeft,int pRight,int iLeft, int iLeft,t){
        if(pLeft>pRight){
            return null;
        }
        //这个是我们根节点在我们前序遍历的位置
        int pRoot = pLeft;
        //这个是我们根节点在我们中序遍历的位置
        int iRoot = map.get(preorder[pLeft]);
        //创建我们的根节点
        TreeNode root = new TreeNode(preorder[pRoot]);
        //通过我们的中序的遍历根节点的位置确定我们的左子树的节点数量
        int leftSize = iRoot - iLeft;
        //通过递归的方式,慢慢确定节点位置,这是一种自顶而下的方式
        //pLeft+1:因为第一个是我们的根节点,向右移位(确定新的先序遍历左边界)
        //pLeft+leftSize:左边界加上左子树节点数量(确定新的先序遍历右边界)
        //在中序遍历中与前序遍历先对应的边界
        //iLeft:左边界
        //根节点的左侧
        root.left = buildTree(preorder,map,pLeft+1,*pLeft+l*eftSize,iLeft,iRoot-1);
        //我们刚刚分割了前半部分,这回就是我们的后半部分
        root.right = buildTree(preorder, map, pLeft+leftSize+1,pRight, iRoot+1, iRight);
        return root;

    

其实,整个的代码的过程就是我们不断地先确定他的根节点→左子树,当左侧完成之后,就是小树的右子树,向上结束