leetcode刷题day16|二叉树Part04(找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树)

找树左下角的值

题目的核心问题:在树的最后一行找到最左边的值。

递归思路:要找到树的最后一行最左边的值,不能单纯的一路向左递归。要先找到最后一行。只要以左节点优先遍历的递归均可,这里选用先序遍历。遍历找到叶子节点,记录最大深度,每找到一个叶子节点,比较其深度和最大深度,如果其深度大于最大深度,更新最大深度和结果。最后返回结果即可。所以要定义两个全局变量:最大深度和结果。

1、传入参数:传入根节点和层数,有全局变量所以无需返回值;
2、终止条件:节点为空,返回;
3、递归函数:如果节点为叶子节点,更新最大深度和结果。如果节点的左孩子存在,深度++,递归左孩子,深度–(回溯);如果节点的右孩子存在,深度++,递归右孩子,深度–(回溯)。
代码如下:

class Solution {
    int maxDeep=-1;
    int result=0;
    public int findBottomLeftValue(TreeNode root) {
        result=root.val;
        findLeftValue(root,0);
        return result;
    }
    public void findLeftValue(TreeNode root,int deep){
        if(root==null) return;
        if(root.left==null && root.right==null){
            if(deep>maxDeep){
                maxDeep=deep;
                result=root.val;
            }
        }
        if(root.left!=null){
            deep++;
            findLeftValue(root.left,deep);
            deep--;
        }
        if(root.right!=null){
            deep++;
            findLeftValue(root.right,deep);
            deep--;
        }
    }
}

迭代思路:层次遍历。使用层次遍历只需要找到最后一行,输出第一个元素的值即可。
代码如下:

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        int result=root.val;
        while(!queue.isEmpty()){
            int size=queue.size();
            for(int i=0;i<size;i++){
                TreeNode node=queue.poll();
                if(i==0) result=node.val;//记录每一层的第一个元素
                if(node.left!=null) queue.offer(node.left);
                if(node.right!=null) queue.offer(node.right);
            }
        }
        return result;
    }
}

路径总和

递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:

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

112. 路径总和

这个题目一眼递归+回溯。只需要确定是否存在这样一条路径满足条件,返回boolean类型
选择先序遍历,递归三部曲:
1、传入参数:root根节点,传入目标值target。不要遍历整棵树,所以递归函数需要返回值,返回值boolean类型。使用原函数即可。
2、终止条件:如果节点为空,直接返回false(根节点为空的情况);节点为叶子节点(左右孩子为空),返回target==root.val;
3、递归逻辑:如果左孩子存在,target减去节点值,如果遍历左孩子的结果为true直接返回true,target加上节点值(回溯);如果右孩子存在,target减去节点值,如果遍历右孩子的结果为true直接返回true,target加上节点值(回溯);

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root==null) return false;//根节点为空的情况
        if(root.left==null && root.right==null) return targetSum==root.val;
        boolean left=hasPathSum(root.left,targetSum-root.val);
        boolean right=hasPathSum(root.right,targetSum-root.val);
        return left || right;
    }
}

113. 路径总和 II

需要返回所有符合条件的路径。
递归三部曲:
1、传入参数:根节点root,目标值target,结果列表result,路径列表path;直接加入result表中,无返回值
2、终止条件:当节点为叶子节点且目标值减去当前节点的值结果是0时,将该路径加入result表中。否则直接return。
3、递归函数逻辑:首先将遍历的节点加入path中,如果左孩子存在,递归调用作节点,并传入目标值减去当前节点值的结果作为目标值(包含了回溯),path中移除最后一个节点(回溯);右孩子同理

class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> result=new ArrayList<>();
        if(root==null) return result;
        List<Integer> path=new LinkedList<>();
        everyPath(root,targetSum,result,path);
        return result;
    }
    public void everyPath(TreeNode root,int targetSum, List<List<Integer>> result,List<Integer> path){
        path.add(root.val);
        if(root.left==null && root.right==null){
            if(targetSum==root.val){
                result.add(new ArrayList<>(path));
            }
            return;
        }
        if(root.left!=null){
            everyPath(root.left,targetSum-root.val,result,path); 
            path.remove(path.size()-1);
        }
        if(root.right!=null){
            everyPath(root.right,targetSum-root.val,result,path);
            path.remove(path.size()-1);
        }
    }
}

从中序与后序遍历序列构造二叉树

106.从中序与后序遍历序列构造二叉树

根据中序与后序遍历序列构造二叉树的步骤:

第一步:如果数组大小为零的话,说明是空节点了。
第二步:如果不为空,那么取后序数组最后一个元素(根节点)作为节点元素。
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
第五步:根据中序数组的大小切割后序数组,切成后序左数组和后序右数组
第六步:递归处理左区间和右区间

那么递归的三部曲分别是什么呢?
由于需要分割数组,所以我们使用HsahMap来记录中序序列的元素和位置。
1、传入参数:中序序列,中序序列的开始和结束位置,后序序列,后序序列的开始和结束位置。返回值为树的节点类型。
2、终止条件:当开始位置大于等于结束位置时,说明数组中不存在元素,return null。
3、递归函数逻辑:首先找到后序数组最后一个元素,在哈希表中找到这个元素在中序序列中的下标,分割左右子树,同时根据中序序列左右子树的元素个数对后序序列进行分割。为后序数组最后一个元素建立节点,为根节点,递归调用函数找到该节点的左孩子和右孩子。

代码:

class Solution {
    HashMap<Integer,Integer> map;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        map=new HashMap<>();
        for(int i=0;i<inorder.length;i++){
            map.put(inorder[i],i);
        }
        return buildNewTree(inorder,0,inorder.length,postorder,0,postorder.length);
    }
    public TreeNode buildNewTree(int[] inorder,int inStart,int inEnd,int[] postorder,int postStart,int postEnd){
        if(inStart>=inEnd || postStart>=postEnd){
            return null;
        }
        int index=map.get(postorder[postEnd-1]);
        int len=index-inStart;
        TreeNode root=new TreeNode(inorder[index]);
        root.left=buildNewTree(inorder,inStart,index,postorder,postStart,postStart+len);
        root.right=buildNewTree(inorder,index+1,inEnd,postorder,postStart+len,postEnd-1);
        return root;
    }
}

注意:在写递归函数时,一定要注意左闭右开区间。

105.从前序与中序遍历序列构造二叉树

同理,前序的第一个元素为中间节点,可以对中序遍历序列进行分割。
代码如下:

class Solution {
    HashMap<Integer,Integer> map;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        map=new HashMap<>();
        for(int i=0;i<inorder.length;i++){
            map.put(inorder[i],i);
        }
        return buildNewTree(preorder,0,preorder.length,inorder,0,inorder.length);
    }
    public TreeNode buildNewTree(int[] preorder,int preStart,int preEnd,int[] inorder,int inStart,int inEnd){
        if(preStart>=preEnd || inStart>=inEnd){
            return null;
        }
        int index=map.get(preorder[preStart]);
        TreeNode root=new TreeNode(inorder[index]);
        int lenOfLeft=index-inStart;
        root.left=buildNewTree(preorder,preStart+1,preStart+1+lenOfLeft,inorder,inStart,index);
        root.right=buildNewTree(preorder,preStart+1+lenOfLeft,preEnd,inorder,index+1,inEnd);
        return root;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值