代码随想录刷题|LeetCode 513. 找树左下角的值 112. 路径总和 113.路径总和|| 106. 从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树

简介: 代码随想录刷题|LeetCode 513. 找树左下角的值 112. 路径总和 113.路径总和|| 106. 从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树

513.找树左下角的值

题目链接:力扣

思路

 层序遍历的思路还是很好得到的,在每层的遍历中我们都可以得到最左边的数字,那么也是可以得到最底层的最左边的数字的,比递归法简单多了


       使用递归的话也是可以找到最底层最左侧的值——最后一行找到最左侧的值,我们只要找到这棵树得最大深度,然后记录这层从左侧第一个值就可以了

       那么前序(中左右)、后序(左右中)、中序(左中右),都是可以的,因为不论是那种顺序的遍历,每一层都是会先处理左边再处理右边,这道题目没有中间节点处理的逻辑

       所以通过递归要做两件事:

               1、找到树的最大深度

               2、记录最大深度的最左侧的值

找树左下角的值

递归法

 定义一个变量,表示最大深度,记录所有深度中的最大深度

       定义一个变量,表示结果,每层记录结果,最后结果中保存的是深度最大的一层的结果


       递归函数:

               处理根节点的参数,还有一个参数depth,记录当前深度


       终止条件:

               当遇到叶子节点的时候,就需要统计一下最大的深度了,并且更新值

               通俗的想,直到找到最底层、最左侧的节点,我们就可以进行终止了

               在遍历的时候,不论是哪种序的遍历方法,对当前层来说,总是左节点先进来判断的

               而这里利用这个特性,深度加深,此层的第一个节点进来,判断,记录当前最大深度,然后记录节点的值,此时深度更新完毕,最左侧节点更新完毕


class Solution {
    public int findBottomLeftValue(TreeNode root) {
        // 根节点和其对应的深度
        traversal(root,1);
        return result;
    }
    int maxDepth = Integer.MIN_VALUE; //用来存放最大深度的
    int result = 0; // 当深度增加时,保存这一层的第一个值
    // 首先确定递归函数
    void traversal(TreeNode node, int depth) {
        if (node == null) {
            return;
        }
        if (node.left == null && node.right == null) {
            // 说明node是一个叶子节点
            if (depth > maxDepth) {
                maxDepth = depth;
                result = node.val;
            }
        }
        // 左
        if (node.left != null) {
            // 那就得探探你的深度了,找到你这边深度最大的叶子节点了
            // 注意哈:traversal()的两个参数代表的意思是;这个节点,这个节点的深度
            depth++;
            traversal(node.left,depth);
            depth--;
        }
        // 右
        if (node.right != null) {
            depth++;
            traversal(node.right,depth);
            depth--;
        } 
        // 中
        // 中不做处理
    }
}

迭代法

       层序遍历,简单易懂好操作

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        // 设这个要查找的值为0,使用层序遍历
        // 每次遍历一层就将这个值重新赋值一下
        int result = 0;
        Deque<TreeNode> deque = new ArrayDeque<>();
        deque.offer(root);
        while (!deque.isEmpty()) {
            int len = deque.size();
            TreeNode leftnode = deque.peek();
            result = leftnode.val;
            for (int i = 0; i < len; i++) {
                TreeNode node = deque.poll();
                if (node.left != null) {
                    deque.offer(node.left);
                } 
                if (node.right != null) {
                    deque.offer(node.right);
                }
            }
        }
        return result;
    }
}

112.路径总和

题目链接:力扣

思路

求出所有根节点到叶子节点的值的总和,然后看看其中有没有和目标值相等的值

       那就需要收割所有路径的值,这跟 257.二叉树的所有路径 比较像,只不过多了一步处理

路径总和

递归法(全部遍历)

 太激动了,第一次自己完整地写出来一道递归的题目

根据257的写法:

       这种方法对于求所有的路径是很合适的,因为你一定要把所有的路径都添加进去。

       但是这个题目没必要把左右的路径全部算出来,只要有符合路径的结果就可以了

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        List<TreeNode> nodes = new ArrayList<>();
        List<Integer> allValue = new ArrayList<>();
        if (root == null) {
            return false;
        }
        nodesSum(root,nodes,allValue);
        return allValue.contains(targetSum);
    }
    void nodesSum(TreeNode node, List<TreeNode> nodes, List<Integer> allValue) {
        // 中
        nodes.add(node);
        // 终止条件
        if (node.left == null && node.right == null) {
            // 说明碰到这条路径的最后叶子节点了,收集这条路径上的结果
            int sum = 0;
            for (TreeNode value : nodes) {
                sum += value.val;
            }
            allValue.add(sum);
        }
        // 左
        if (node.left != null) {
            nodesSum(node.left,nodes,allValue);
            nodes.remove(nodes.size() - 1);
        }
        // 右
        if (node.right != null) {
            nodesSum(node.right,nodes,allValue);
            nodes.remove(nodes.size() - 1);
        }
    }
}

递归法(不用全部遍历)

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return false;
        }
        return traversal(root,targetSum - root.val);
    }
    boolean traversal(TreeNode cur,int count) {
        // 终止条件
        // 遇到了叶子节点,并且目标值被减到0
        if (cur.left == null && cur.right == null && count == 0) {
            return true;
        }
        // 遇到了叶子节点,但是目标值没有被减到0
        if (cur.left == null && cur.right == null) {
            return false;
        }
        // 左
        if (cur.left != null) {
            count -= cur.left.val;
            if (traversal(cur.left,count)) {
                return true;
            }
            count += cur.left.val;
        }
        // 右
        if (cur.right != null) {
            count -= cur.right.val;
            if (traversal(cur.right,count)) {
                return true;
            }
            count += cur.right.val;
        }
        return false;
    }
}

113.路径总和||

题目链接:力扣

思路

       跟257、二叉树的所有路径 力扣 112 路径总和 力扣 的原理是一样的,只是对结果的处理上有所不同

路径总和||

 需要注意的点是:

               在将List<Integer>添加到List<List<Integer>>中时,一定要新创建一个集合来保存结果,否则这个集合会被后面的程序改变,因为集合中保存的是内存地址

    <--------错误实例:  


07b39f81df2c49fba42579a76329b2df.png

class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> nodeVals = new ArrayList<>();
        if (root == null) {
            return  result;
        }
        nodesList(root,nodeVals,result,targetSum);
        return result;
    }
    void nodesList(TreeNode node,List<Integer> nodeVals,List<List<Integer>> result,int targetSum){
        // 中
        nodeVals.add(node.val);
        // 终止条件
        if (node.left == null && node.right == null) {
            int sum = 0;
            for (Integer nodeVal : nodeVals) {
                sum += nodeVal;
            }
            if (sum == targetSum) {
                // 要重新创建一个集合对象来保存这里的值,集合中只能保存地址
                result.add(new ArrayList<>(nodeVals));
            }
        }
        // 左
        if (node.left != null) {
            nodesList(node.left,nodeVals,result,targetSum);
            nodeVals.remove(nodeVals.size() - 1);
        }
        // 右
        if (node.right != null) {
            nodesList(node.right,nodeVals,result,targetSum);
            nodeVals.remove(nodeVals.size() - 1);
        }
    }
}

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

题目链接:力扣

思路

分为6步:

1、后序数组为0,空节点

       2、后序数组最后一个元素为节点元素

       3、寻找中序数组位置作切割点

       4、切中序数组

       5、切后序数组

       6、递归处理左区间、后区间


分解一下:

       1、后序数组都是空了,就没有中了,就是空节点

       2、后序数组中的最后一个元素是根节点的元素,根据根节点元素创建二叉树

       3、切割点怎么找:遍历中序数组,找到根节点元素所在的下标位置

       4、切割中序数组:为了得到左中序数组和右中序数组

       5、切割后序数组:利用左中序数组进行切割,得到左后序数组和右后序数组

       6、递归处理分割出来的数组

               左节点 =递归函数(左中序数组,左后序数组)

               右节点 =递归函数(右中序数组,右后序数组)


这道题目的整体思路是不难的,难的是代码的书写,比较容易转不去来,多写几遍,手动运行一下代码会更好理解


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

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if (inorder.length == 0 || postorder.length == 0) {
            return null;
        }
        // 左闭右开的原则
        return traversal(inorder,0,inorder.length,postorder,0,postorder.length);
    }
    // 因为递归的时候,要传递两个数组的其实位置和结束位置,需要确定递归函数
    public TreeNode traversal(int[] inorder, int inBegin, int inEnd,int[] postorder,int postBegin,int postEnd) {
        // 1、后序数组为0,空节点
        if (postBegin == postEnd) {
            return null;
        }
        // 2、后序数组最后一个元素为节点元素
        int nodeValue = postorder[postEnd - 1];
        TreeNode root = new TreeNode(nodeValue);  // 构造节点
        // 3、寻找中序数组位置作切割点
        // 遍历中序数组,找出和后序数组中最后一个元素相同的元素下标
        int index;
        for (index = inBegin; index < inEnd; index++) {
            if (inorder[index] == nodeValue) {
                break;
            }
        }
        // 4、切割中序数组
        // 左中序区间,左闭右开[inBegin,index)
        int leftInBegin = inBegin;
        int leftInEnd = index; // 这里是开,左区间是取不到的
        // 右中序区间,左闭右开[index + 1,inEnd)
        int rightInBegin = index + 1;
        int rightInEnd = inEnd;
        // 5、切割后序数组
        // 中序数组的左中序数组就是后序数组的左后序数组
        // 左后序数组,左闭右开[postBegin,postBegin + (左中序区间的长度))
        int leftPostBegin = postBegin;
        int leftPostEnd = postBegin + (index - inBegin);
        // 右后序数组,左闭右开[postBegin + (index - inBegin),postEnd - 1)
        int rightPostBegin = postBegin + (index - inBegin);
        int rightPostEnd = postEnd - 1; // 排除最后一个元素 这个元素已经成为节点了
        // 6、递归处理左区间、后区间
        root.left = traversal(inorder,leftInBegin,leftInEnd,postorder,leftPostBegin,leftPostEnd);
        root.right = traversal(inorder,rightInBegin,rightInEnd,postorder,rightPostBegin,rightPostEnd);
        return root;
    }
}

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

题目链接:力扣

思路

       与106的题目思路相同

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

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder.length == 0 || inorder.length == 0) {
            return null;
        }
        return treaversal(preorder,0,preorder.length,inorder,0,inorder.length);
    }
    public TreeNode treaversal(int[] preorder,int preBegin,int preEnd,int[] inorder,int inBegin, int inEnd) {
        // 1、处理空数组
        if (preBegin == preEnd) {
            return null;
        }
        // 2、取前序数组的第一个元素
        int nodeValue = preorder[preBegin];
        TreeNode node = new TreeNode(nodeValue);
        // 3、根据中节点分割中序数组
        int index;
        for (index = inBegin;index<inEnd;index++) {
            if (inorder[index] == nodeValue) {
                break;
            }
        }
        // 4、切割中序数组
        // 左中序数组
        int leftInBegin = inBegin;
        int leftInEnd = index;
        // 右中序数组
        int rightInBegin = index + 1;
        int rightInEnd = inEnd;
        // 5、切割前序数组
        // 左前序数组
        int leftPreBegin = preBegin + 1;
        int leftPreEnd = preBegin + 1 + (index - inBegin);
        // 右前序数组
        int rightPreBein =  preBegin + 1 + (index - inBegin);
        int rightPreEnd = preEnd;
        // 6、递归数组
        node.left = treaversal(preorder,leftPreBegin,leftPreEnd,inorder,leftInBegin,leftInEnd);
        node.right = treaversal(preorder,rightPreBein,rightPreEnd,inorder,rightInBegin,rightInEnd);
        return node;
    }
}
相关文章
|
1月前
【LeetCode 43】236.二叉树的最近公共祖先
【LeetCode 43】236.二叉树的最近公共祖先
18 0
|
1月前
【LeetCode 38】617.合并二叉树
【LeetCode 38】617.合并二叉树
14 0
|
1月前
【LeetCode 37】106.从中序与后序遍历构造二叉树
【LeetCode 37】106.从中序与后序遍历构造二叉树
16 0
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
56 6
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
50 4
|
3月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
113 2
|
19天前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
16 1
|
2月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
3月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
56 7