day15找树左下角的值&路经总和&从中序与后序遍历序列构建二叉树(递归详解版及总结)

发布于:2022-11-29 ⋅ 阅读:(405) ⋅ 点赞:(0)

        树的更层知识,算是把之前学的理论都实践了,有点难度想了挺久的。

1.力扣513(找树左下角的值)

                                b251a04ebdca485eb51cf104f5becdac.png       

        本题依旧利用递归三部曲来解决,我们要先确定遍历顺序,我们需要找的节点是左下角的值,所以我们应该利用的遍历顺序是把左放在前面的遍历顺序就可以,所以前中后序遍历都可以;接下来我们要取得节点应满足深度最大,并且在深度相同的情况下取最边的值

1.递归法

接下来我们利用递归三部曲来解决问题:

  • 确定递归函数的参数和返回值:参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。 这里就不需要返回值了,所以递归函数的返回类型为void(本题还需要类里的两个全局变量,用maxDepth来记录最大深度,res记录最大深度最左节点的数值。)
    int maxDepth = Integer.MIN_VALUE;
    int res = 0;
    public void findValue(TreeNode root,int depth) 
  • 确定终止条件:当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度,在更新深度的同时进行判断,若深度最大我们就要更新数值。
        if(root.left==null&&root.right==null){
            if(depth>maxDepth){//若深度最大就更新结果
                maxDepth=depth;
                res=root.val;
            }
        }
  • 确定单层递归的逻辑:在找最大深度的时候,递归的过程中依然要使用回溯,因为每个节点的深度是变化的。(可以理解下回溯的过程,第二种写法更好理解)
        if(root.left!=null){
            //回溯的过程
            findValue(root.left,depth+1);
            //第二种写法
//            depth++;
//            findValue(root.left,depth);
//            depth--;
        }
        if(root.right!=null){
            //回溯的过程
            findValue(root.right,depth+1);
            //第二种写法
//            depth++;
//            findValue(root.right,depth);
//            depth--;
        }

2.迭代法

        本题的迭代法也就是二叉树的层序遍历,只是我们在遍历至最后一层时,我们把最后一层的首元素进行弹出操作,代码好写,思路的话只要理解了层序遍历就会很快的写出来。(有代码注释)

下面是代码:

    public int findBottomLeftValue(TreeNode root) {
        //创建队列
        Queue<TreeNode> queue = new LinkedList<>();
        if(root==null){
            return 0;
        }
        //存入根节点
        queue.offer(root);
        //存结果
        List<List<Integer>> res = new ArrayList<>();
        while(!queue.isEmpty()){、
            //每一层的list集合
            List<Integer> temp = new ArrayList();
            int size = queue.size();
            //遍历出每一层的元素
            while(size>0){
                //弹出元素
                TreeNode node = queue.poll();
                temp.add(node.val);
                //存入弹出节点的左孩子
                if(node.left!=null){
                    queue.offer(node.left);
                }
                //存入弹出节点的右孩子
                if(node.right!=null){
                    queue.offer(node.right);
                }
                size--;
            }
            res.add(temp);
        }
        //取出结果集中的尾集合的首元素
        return res.get(res.size()-1).get(0);
    }

2.力扣112(路经总和)

        本题学到了很重要的知识,就是什么时候进行返回结果,什么时候不需要返回:

  • 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
  • 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先 (opens new window)中介绍)
  • 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)

                               69e15dd245d04028a8a644a7e659ebf8.png

        本题继续利用递归遍历整个二叉树,若我们遍历到叶子节点的时候,我们就需要判断这个路径的和是否是满足我们的目标和。接下来我们利用递归三部曲来解决:

  • 确定递归函数的参数和返回类型参:需要二叉树的根节点,还需要一个计数器,这个计数器用来计算二叉树的一条边之和是否正好是目标和,计数器为int型。
    public boolean findSum(TreeNode root, int count)
  • 确定终止条件:我们可以利用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。如果最后count == 0,同时到了叶子节点的话,说明找到了目标和。如果遍历到了叶子节点,count不为0,就是没找到。
        if(root.left==null&&root.right==null&&count==0){//满足条件
            return true;
        }
        if(root.left==null&&root.right==null){//不满足返回false
            return false;
        }
  • 确定单层递归的逻辑:因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了,递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回。
        //遍历左子树
        if(root.left!=null){
            //若左子树满足就直接返回
            if(findSum(root.left,count-root.left.val)){
                return true;
            }
        }
        //遍历右子树
        if(root.right!=null){
            //若右子树满足就直接返回
            if(findSum(root.right,count-root.right.val)){
                return true;
            }
        }
        //若不满足就返回false
        return false;

整体代码:

    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root==null){
            return false;
        }
        return findSum(root,targetSum-root.val);
    }
    public boolean findSum(TreeNode root, int count) {
        if(root.left==null&&root.right==null&&count==0){
            return true;
        }
        if(root.left==null&&root.right==null){
            return false;
        }
        //遍历左子树
        if(root.left!=null){
            //若左子树满足就直接返回
            if(findSum(root.left,count-root.left.val)){
                return true;
            }
        }
        //遍历右子树
        if(root.right!=null){
            //若右子树满足就直接返回
            if(findSum(root.right,count-root.right.val)){
                return true;
            }
        }
        //若不满足就返回false
        return false;
    }

3.力扣106(从中序与后序遍历序列构建二叉树)

                               21ebcdf8f3884a66874d9279c57e56fe.png

        本题也是利用递归,首先我们要知道通过遍历序列来构建二叉树只能有两种:前序和中序,中序和后序。明白了这点后我们来解决本题,构建二叉树的思路就是,我们通过前序或者后序来找出中节点,然后通过中节点来切割中序数组,然后再来切割后续数组,接着我们再把切割的数组进行递归创建。我这里把卡哥的图片放上,很好理解

        c6379d44fbdf43d0a66f86a26297d1a6.png

 我们再切割好前序数组和后序数组后,把前序数组的左和后续数组的左进行递归构建左子树,同理来构建右子树。

有卡哥总结的几步构建二叉树的步骤

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

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

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

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

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

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

 下面是实现代码(标注了步骤):

    public TreeNode buildTree(int[] inorder,int[] postorder) {
        if(inorder.length==0||postorder.length==0){
            return null;
        }
        return travleTree(inorder,postorder);
    }

    public TreeNode travleTree(int[] inorder,int[] postorder) {
        // 1.如果后序数组大小为零的话,说明是空节点了。
        if(postorder.length==0){
            return null;
        }
        //2.那么取后序数组最后一个元素作为节点元素。
        int rootValue = postorder[postorder.length-1]; 
        TreeNode root = new TreeNode(rootValue);
        //3.后序数组最后一个元素在中序数组的位置,作为切割点
        int index = 0;
        for(;index<inorder.length;index++){
            if(inorder[index]==rootValue){
                break;
            }
        }
        //4.切割中序数组,切成中序左数组和中序右数组
        int[] leftInoreder = Arrays.copyOfRange(inorder,0,index);
        int[] rightInoreder = Arrays.copyOfRange(inorder,index+1,inorder.length);
        //5.切割后序数组,切成后序左数组和后序右数组
        int[] leftPostorder = Arrays.copyOfRange(postorder,0,index);
        int[] rightPostorder = Arrays.copyOfRange(postorder,index,postorder.length-1);      
        //6.递归处理左区间和右区间
        root.left = buildTree(leftInoreder,leftPostorder);
        root.right = buildTree(rightInoreder,rightPostorder);
        return root;
    }

 

 

 

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到