代码随想录算法训练营day18|找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树

2023.4.1 找树左下角的值

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

方法一 先递归找最大深度再层序遍历

直观的思路,先通过递归求出二叉树的最大深度,再通过层序遍历找到对应层的第一个节点。

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        int depth = getDepth(root);
        //开始层序遍历
        Queue<TreeNode> queue = new LinkedList<>();
        if(depth == 1){
            return root.val;
        }
        else{
        int res = 0; 
        queue.offer(root);
        int layer = 1 ;
        while(!queue.isEmpty()){
            int length = queue.size();
            layer++;
            for(int i = 0;i<length;i++){
                TreeNode temp = queue.poll();
                if(temp.left != null){
                    queue.offer(temp.left);
                }
                if(temp.right != null){
                    queue.offer(temp.right);
                }
            }
            if(layer == depth  ){
                res = queue.poll().val;
            }
        }
        return res;
    }
        
    }
    public int getDepth(TreeNode root){
        if(root == null){
            return 0;
        }
        int leftDepth = getDepth(root.left);
        int rightDepth = getDepth(root.right);
        return Math.max(leftDepth,rightDepth) + 1;
    }
}

方法二 借助链表记录数值的层序遍历

我们可以借助链表吧每一层的值给记录下来,这样退出循环的时候,链表中的第一个值就是我们需要的结果

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        int res = 0;
        queue.offer(root);
        while(!queue.isEmpty()){
            //每一层的遍历我们都重新建立一个链表,来保证最后输出时链表中存放的就是最后一层的元素
            List<Integer> list = new ArrayList<>();
            int length = queue.size();
            for(int i = 0;i<length;i++){
                TreeNode temp = queue.poll();
                list.add(temp.val);
                 if(temp.left != null){
                    queue.offer(temp.left);
                }
                if(temp.right != null){
                    queue.offer(temp.right);
                }
            }
            //每层的循环结束时,我们链表中的第一个元素存放的就是我们这层最左边的值
            res = list.get(0);
        }
        
        return res;
    }
}

方法三 从右到左的层序遍历

我们之前的层序遍历规定的都是从左到右,导致想要找到最左边的节点时无法确定位置,这里我们可以考虑构建一个从右到左的层序遍历,每次遍历时将当前节点的值存储进变量中,这样最后得到的结果一定是最底层最左边的那个元素。

这种方法会增加时间复杂度,实际上我们可以直接采用层序遍历即可得到结果。

层序遍历时,将每一层的节点值存入一个链表中,这样,当结束循环时,我们的链表的第一个值就是我们想要的值

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        int res = 0;
        queue.offer(root);
        while(!queue.isEmpty()){
            //每一层的遍历我们都重新建立一个链表,来保证最后输出时链表中存放的就是最后一层的元素
            int length = queue.size();
            for(int i = 0;i<length;i++){
                TreeNode temp = queue.poll();
                
                if(temp.right != null){
                    queue.offer(temp.right);
                }
                 if(temp.left != null){
                    queue.offer(temp.left);
                }
               res = temp.val;
            }
        }
        return res;
    }
}

方法四 递归方法

我们使用深度优先遍历的方法,遍历时记录当前层数,每进入一层更新最大层数,同时记录下当前的节点值。因为我们是先记录的左子节点,因此当层数更新时,一定是记录的最左侧的节点(如果没有左子节点,代表此时最深层只有一个右子节点,此时成为了我们最左侧的节点)

class Solution {
    int maxLevel = 0; 
    int res = 0;
    public int findBottomLeftValue(TreeNode root) {
        dfs(root,0);
        return res;
    }
    public void dfs(TreeNode root,int level){
        //退出条件,当前节点为null
        if(root == null){
            return;
        }
        //否则就代表进入新的一层,使层数+1;
        level++;
        //随后先遍历左子节点,再遍历右子节点
        dfs(root.left,level);
        dfs(root.right,level);
        //遍历到此处,代表遍历到了一个根节点,此时我们记录其值,并更新最大深度
        if(level > maxLevel){
            maxLevel = level;
            res = root.val;
        }
        //这样,当遍历到最深层时,maxlevel与当前层数相等,不会再更新,因此只会进入if语句一次,并且进入if语句的这个叶节点就是我们的最深层的最左侧节点,因为我们是先进入的左子节点。

    }
}

2023.4.1 路径总和

112. 路径总和 - 力扣(LeetCode)

能否先求出所有路径对应的数值和,计入哈希表中,再根据target找出存在与否?

第一版 遍历所有路径+哈希

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        HashMap<Integer,Integer> hashMap = new HashMap<>();
        getPath(root,0,hashMap);
        if(root == null){
            return false;
        }
        if(hashMap.containsKey(targetSum)){
            return true;
        }
        else{
            return false;
        }
    }
    public void getPath(TreeNode root,int sum,HashMap<Integer,Integer> hashMap){
        if(root == null){
            return;
        }
        int oneSum = sum + root.val;
        //进入了叶子结点
        if(root.left == null && root.right == null){
            //结束路径判断,将当前的路径sum值进入hashMap;
            hashMap.put(oneSum,hashMap.getOrDefault(oneSum,0)+1);
        }
        //不是叶子结点,继续进行深度优先遍历
        else{
            getPath(root.left,oneSum,hashMap);
            getPath(root.right,oneSum,hashMap);
        }
    }
}

方式二 直接递归

实际上,我们并不需要哈希表来记录每个路径的值,我么你只需要记录下当满足条件时,我们将一个变量赋予目标值,最后对其进行相等与否的判断即可。

class Solution {
    int res = -1001;
    public boolean hasPathSum(TreeNode root, int targetSum) {
        getPath(root,0,targetSum);
        if(res == targetSum){
            return true;
        }
        return false;
       
    }
    public void getPath(TreeNode root,int sum,int targetSum){
        if(root == null){
            return;
        }
        int oneSum = sum + root.val;
        //进入了叶子结点
        if(root.left == null && root.right == null && oneSum == targetSum){
            //满足条件,将变量赋予正确值。
                res = targetSum;
        }
        //不是叶子结点且路径值不相等,继续进行深度优先遍历
        else{
            getPath(root.left,oneSum,targetSum);
            getPath(root.right,oneSum,targetSum);
        }
    }
}

方式三 更简洁的递归

实际上,我们同样是深度优先遍历,题目要求一条路径上的所有数值之和为target,因此,我们可以在深度优先遍历时,每遍历一个节点,就将target减去当前节点值,直到叶子结点,检查当前值是否等于0,如果为0,代表有路径满足。

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null){
            return false;
        }
        else if(root.left == null && root.right == null && targetSum - root.val == 0){
            return true;
        }
        //没有到达叶子结点,继续进行判断。
        return hasPathSum(root.left,targetSum-root.val) || hasPathSum(root.right,targetSum-root.val);
    }
}

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

106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

中序遍历得到的数组中,我们能够得到的信息就是第一个元素一定是左子节点,但是我们并不能确定根节点时哪一个

而后序遍历得到的数组中,我们可以根据最后一个元素来确定根节点。因此,我们可以先找到后续遍历数组中的最后一个元素,确定总的根节点的位置,随后在中序数组中找到这个根节点的位置,将其划分为左右区间,这样,我们就可以找到左子树的根节点与右子树的根节点了,方法就是根据中序数组根节点划分出来的左子树区间,找到起长度大小,在后序数组中找到对应长度的最后一个元素2,就是我们左子树的根节点,随后再次重复这个过程,即找到左子树根节点后,继续划分左子树的左区间和右区间。而根节点的右区间就是就是从我们中序数组根节点划分出来的右半部分,以及后续数组去掉左区间和最后一位的部分,在这之中,再通过后续数组右区间的最后一个元素来确定右区间的根节点,再划分中序数组右子树的左区间和右区间,重复这个过程。

梳理一下大概的步骤,可以划分为以下几步:

1.找到后续数组的最后一个元素即为当前根节点,然后在中序数组中确定根节点的位置(这里可能需要使用hashMap方便查找)

2.确定根节点为之后我们要找到根节点的左子树根节点和右子树根节点。

3.左子树根节点:找到中序数组的根节点后,我们就能确定左子树的区间,找到其长度,用来确定后序数组中左子树区间的位置,随后在后续数组中的左子树区间继续寻找最后一个元素,重复这个过程,即在中序数组中找到对应的元素,就是我们左子树的根节点。

4.右子树根节点:通过后续数组最后一个元素找到根节点之后,我们就能确定前序数组右子树的区间,即从确定的根节点的位置到最后一位。同样我们可以确定后序数组的右子树区间,就是确定好左子树范围后,其右半部分到最后一个节点之前的位置。

此时我们再从后续数组的右区间节点的最后一个节点确定为右子树的根节点,再次在中序数组中找到根节点的位置,重复这个过程。

5.什么时候退出? 当我们的左右区间不满足大小关系时就需要退出,即返回null值。

class Solution {
    //为了能够方便的从中序数组中找到根节点的位置,我们需要将中序数组的节点元素存入哈希数组中。它的初始化在buildTree方法中完成
    HashMap<Integer,Integer> map;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        //将我们中序数组的元素计入hashMap
            map = new HashMap<>();
        for(int i = 0;i<inorder.length;i++){
        
            map.put(inorder[i],i);
        }
        return getRoot(inorder,0,inorder.length,postorder,0,postorder.length);
    }
    //构建一个递归找到根节点的方法,返回类型为TreeNode,传入的参数,需要传入中序数组,以及中序数组对应的起点和终点,同样的有后续数组以及后续数组对应的起点和终点
    public TreeNode getRoot(int[] inorder,int inorderBegin,int inorderEnd,int[] postorder,int postBegin,int postEnd){
        //结束递归的条件:当我们的左区间大于等于右区间,就没有意义了 ,因为我们设置的区间是左闭右开的,一旦左区间等于了右区间就代表没有意义了。
        if(inorderBegin >= inorderEnd || postBegin >= postEnd){
            return null;
        }
        //首先找到中序数组的根节点的位置,对应的元素为传入的后序数组中最后一个元素
        //这里涉及到一个问题,就是我们的postEnd区间是否需要有意义,即是否需要确定为左闭右闭还是左闭右开。假设为左闭右开区间,那么我们的postEnd就需要减1处理了
        int rootIndex = map.get(postorder[postEnd - 1]);
        //创建根节点的TreeNode 
        TreeNode root = new TreeNode(postorder[postEnd - 1]);
        //确定左子树的区间
        int leftLength = rootIndex - inorderBegin;
        //递归找到左子树的根节点`
        root.left = getRoot(inorder,inorderBegin,rootIndex,postorder,postBegin,postBegin+ leftLength);
        root.right = getRoot(inorder,rootIndex + 1,inorderEnd,postorder,postBegin+leftLength,postEnd - 1);
        return root;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值