【刷题那些事】Leetcode精选二叉树例题+解析

本文详细解析了LeetCode上的多个二叉树问题,包括计算二叉树所有左叶子之和、找树左下角的值、路径总和I和II、从中序与后序遍历序列构造二叉树、从前序与中序遍历序列构造二叉树以及构造最大二叉树。通过递归和层次遍历等方法,深入理解二叉树操作技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

🙏亲爱的朋友,如果对你有帮助,给博主一个免费的点赞以示鼓励把QAQ☺

在这里插入图片描述

👋计算二叉树所有左叶子之和

leetcode题目链接
在这里插入图片描述

解题思路:这道题我们首先要清楚,什么是左叶子

左叶子:位于父节点的左子树,且自身没有左右节点

如下面这幅画(画的丑见谅0.0)

在这里插入图片描述

2就不是左叶子,虽然他没有子节点,但是他不在1节点的左边

清楚了什么是左叶子,我们就可以真正解决这道题,那么我们可以想到,某节点的左子树可能存在左叶子,它的
右子树也可能存在左叶子,它的左子树的左子树也可能存在左叶子

在这里插入图片描述

我们用深度优先搜索,后序遍历(左右中)判断就会比较方便,只需要一直向子树中递归就行

代码奉上

    public int sumOfLeftLeaves(TreeNode root) {
        if (root==null)return 0;
        int left = sumOfLeftLeaves(root.left);
        int right = sumOfLeftLeaves(root.right);

        int mid = 0;
        if (root.left!=null&&root.left.left==null&&root.left.right==null){
            mid=root.left.val;
        }
        return mid+left+right;
    }

在这里插入图片描述

👋找树左下角的值

leetcode题目链接
在这里插入图片描述
在这里插入图片描述

解题思路:本题使用层寻遍历最简单也最方便理解,因为只需要一层一层遍历然后统计最后一行的第一个数据就可以

在这里插入图片描述

    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int left = 0;
        while (!queue.isEmpty()){
            int len = queue.size();
            for (int i=0;i<len;i++){
                TreeNode poll = queue.poll();
                if (i==0) left=poll.val;
                if (poll.left!=null) queue.offer(poll.left);
                if (poll.right!=null) queue.offer(poll.right);
            }
        }
        return left;
    }

在这里插入图片描述

👋路径总和i

题目链接
在这里插入图片描述

题解思路:

深度优先搜索比较适合本题,我们遍历从根节点到叶子结点的路径,看看综合与targetSum是否相等,在进行递归的时候可以把每个节点的值都记录下来,如果不等的话就进行回溯

既然确定要使用递归,那么就必须确定以下三个要素

  • 确定递归函数的参数返回值
  • 确定终止条件
  • 确定单层递归的逻辑

第一个要素,我们可以确定,参数时肯定要有

  • 根节点root,不然没办法递归了
  • 有一个值count来记录路径的和,返回值的话,我们可以看到leetcode题目上返回值为boolean类型,我们的返回值也会是boolean类型
  • targetSum

第二个要素,终止条件肯定是到达根节点

  • 路径的和与targetSum不相等,终止,进行回溯
  • 路径的和与targetSum相等,终止,直接返回

第三个要素,如果有左子节点就向左递归,如果有右子节点就向右递归,记得回溯的时候把count减去未达标节点的val

    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root==null) return false;
        return check(root,root.val,targetSum);
    }

    private boolean check(TreeNode root, int count,int target) {
        if (root.left==null&&root.right==null&&count!=target){
            return false;
        }
        if (root.left==null&&root.right==null){
            return true;
        }
        if (root.left!=null){
            count+=root.left.val;
            if(check(root.left,count,target)) return true;
            count-=root.left.val;
        }
        if (root.right!=null){
            count+=root.right.val;
            if (check(root.right,count,target))return true;
            count-=root.right.val;
        }
        return false;
    }

在这里插入图片描述

👋路径总和ii

leetcode题目链接
在这里插入图片描述

解题思路:

这题和上一题的模式是差不多的但是递归三要素却不一样,我们不仅需要判断值是否满足,还需要把所有符合条件的路径记录下来

我们重新规范一下本题的递归三要素

  • 确定递归函数的参数返回值
  • 确定终止条件
  • 确定单层递归的逻辑

第一个要素,除了root,count,targetSum,我们还需要传进去一个集合Biglist用来保存所有正确的路径,还需要一个集合Smllist用来记录每次经过的节点值,递归不需要返回值

第二个要素,终止条件肯定是到达根节点

  • 路径的和与targetSum不相等,终止,进行回溯
  • 路径的和与targetSum相等,终止,将路径记录在集合中

第三个要素和上一题相同

在这里插入图片描述

    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> Biglist = new ArrayList<>();
        if (root==null)return Biglist;
        List<Integer> smllist = new ArrayList<>();
        check(root,targetSum,Biglist,smllist);
        return Biglist;
    }

    private void check(TreeNode root, int targetSum, List<List<Integer>> biglist, List<Integer> smllist) {
        smllist.add(root.val);
        if (root.left==null&&root.right==null){
            if (targetSum-root.val==0){
                biglist.add(new ArrayList<>(smllist));
            }else {
                return;
            }
        }

        if (root.left!=null){
            check(root.left,targetSum-root.val,biglist,smllist);
            smllist.remove(smllist.size()-1);
        }
        if (root.right!=null){
            check(root.right,targetSum-root.val,biglist,smllist);
            smllist.remove(smllist.size()-1);
        }
    }

在这里插入图片描述

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

leetcode题目链接
在这里插入图片描述

题解思路:观察后序遍历,我们发现后序遍历数组的最后一个元素就是根节点n,而n在中序遍历的位置刚好将切割中序遍历数组,将其一分为二,n的左边为左子树,n的右边为右子树,然后进行递归

在这里插入图片描述
以中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9,15,7,20,3] 为例

  • 第一步找到3在inorder中的位置,位置为1
  • 将inorder以索引1为分界点进行切割[9],[15,20,7]分别进行递归
  • 当数组中只有一个元素时证明它是叶子结点,直接返回
  • 当数组中有多个元素时例如[15,20,7],以20在中序数组中的位置再次进行切割递归,直到找到叶子结点
    public Map<Integer,Integer> map = new TreeMap<>();
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        //放到map中方便寻找下标
        for (int i = 0; i < inorder.length; ++i) {
            map.put(inorder[i],i);
        }
        return Mybuild(inorder,0,inorder.length-1,postorder,0,postorder.length-1);
    }

    private TreeNode Mybuild(int[] inorder, int inLeft, int inRight, int[] postorder, int posLeft, int posRight) {
        if (inLeft>inRight){
            return null;
        }
        int index = map.get(postorder[posRight]);//获得后序遍历数组中节点在中序遍历中的位置

        TreeNode root = new TreeNode(postorder[posRight]);

        root.left = Mybuild(inorder,inLeft,index-1,postorder,posLeft,posLeft+(index-inLeft)-1);
        root.right = Mybuild(inorder,index+1,inRight,postorder,posLeft+(index-inLeft),posRight-1);

        return root;
    }

在这里插入图片描述

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

leetcode题目链接
在这里插入图片描述

解题思路:解题思路可以仿照第五题,我们发现前序遍历数组的第一个元素是中间节点,由此我们可以得到中间节点在中序遍历数组中的下标index,根据Index将中序遍历数组一分为二,左边即为左子树,右边就是右子树,进行递归

    public TreeNode buildTree(int[] preorder, int[] inorder) {
       return MyBuild(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
    }

    private TreeNode MyBuild(int[] preorder, int preLeft, int preRight,int[] inorder, int inLeft, int inRight) {
        if (inLeft>inRight||preLeft>preRight){
            return null;
        }
        int idx = inLeft;

        //得到首节点下标
        int rootIndex = 0;
        for (int i=inLeft;i<=inRight;i++){
            if (preorder[preLeft]==inorder[i]){
                rootIndex=i;
                break;
            }
        }
        //创建首结点
        TreeNode root = new TreeNode(preorder[preLeft]);

        root.left=MyBuild(preorder,preLeft+1,preLeft+(rootIndex-inLeft),inorder,inLeft,rootIndex-1);
        root.right= MyBuild(preorder,preLeft+(rootIndex-inLeft)+1,preRight,inorder,rootIndex+1,inRight);

        return root;
    }

在这里插入图片描述

👋最大二叉树

leetcode题目链接
在这里插入图片描述

递归调用如下所示:

  • [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
    • [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
      • 空数组,无子节点。
      • [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
        • 空数组,无子节点。
        • 只有一个元素,所以子节点是一个值为 1 的节点。
    • [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
      • 只有一个元素,所以子节点是一个值为 0 的节点。
      • 空数组,无子节点。

首先找到每组中的最大值n的下标index,以index为分界点,[left,index-1]为左子树,[index+1,right]为右子树,分别递归

    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return buildBigTree(nums,0,nums.length-1);
    }

    private TreeNode buildBigTree(int[] nums, int left, int right) {
        if (left>right){
            return null;
        }

        //找出最大值
        int index = left;
        int max = nums[left];

        for (int i = left+1; i <=right; i++) {
            if (nums[i]>max){
                max=nums[i];
                index=i;
            }
        }
        //创建根节点
        TreeNode root = new TreeNode(max);

        root.left = buildBigTree(nums,left,index-1);//0 2
        root.right = buildBigTree(nums,index+1,right);//4 5

        return root;
    }

🙏亲爱的朋友,如果对你有帮助,给博主一个免费的点赞以示鼓励把QAQ☺

在这里插入图片描述

(1)非递归定义 树(tree)是由n(n≥0)个结点组成的有限集合。n=0的树称为空树;n>0的树T: ① 有且仅有一个结点n0,它没有前驱结点,只有后继结点。n0称作树的根(root)结点。 ② 除结点外n0 , 其余的每一个结点都有且仅有一个直接前驱结点;有零个或多个直接后继结点。 (2)递归定义 一颗大树分成几个大的分枝,每个大分枝再分成几个小分枝,小分枝再分成更小的分枝,… ,每个分枝也都是一颗树,由此我们可以给出树的递归定义。 树(tree)是由n(n≥0)个结点组成的有限集合。n=0的树称为空树;n>0的树T: ① 有且仅有一个结点n0,它没有前驱结点,只有后继结点。n0称作树的根(root)结点。 ② 除根结点之外的其他结点分为m(m≥0)个互不相交的集合T0,T1,…,Tm-1,其中每个集合Ti(0≤i<m)本身又是一棵树,称为根的子树(subtree)。 2、掌握树的各种术语: (1) 父母、孩子与兄弟结点 (2) 度 (3) 结点层次、树的高度 (4) 边、路径 (5) 无序树、有序树 (6) 森林 3、二叉树的定义 二叉树(binary tree)是由n(n≥0)个结点组成的有限集合,此集合或者为空,或者由一个根结点加上两棵分别称为左、右子树的,互不相交的二叉树组成。 二叉树可以为空集,因此根可以有空的左子树或者右子树,亦或者左、右子树皆为空。 4、掌握二叉树的五个性质 5、二叉树的二叉链表存储。
<think>好的,用户问的是OD机试目,我需要先弄清楚OD指的是什么。可能是指Online Assessment(在线测评),也就是很多公司在招聘时使用的在线笔试。这类目通常包括编程算法、逻辑等。 首先,我需要确认用户的具体需求。他们可能是在准备面试,想了解这类机试的型、难度、考察重点,或者需要备考建议。用户可能没有明确说明,但深层需求可能是如何有效准备这类考试。 接下来,我需要回忆常见的OD机试内容。通常包括数据结构算法,比如数组、字符串、链表、树、图等,还有动态规划、贪心算法等。另外可能涉及系统设计,不过对于初级岗位可能较少。还可能包括逻辑和数学,比如概率或数列问。 然后,用户可能想知道具体的例题,这样他们可以更直观地了解目形式。比如LeetCode上的目,或者公司常考的目类型。比如,两数之和、二叉树遍历、最长子串等。需要举几个例子,并说明解答思路。 还要注意用户提到的格式要求,数学表达式要用$...$,独立公式用$$...$$。所以在举例算法时,如果有时间复杂度说明,需要用LaTeX格式,例如$O(n^2)$。 另外,用户可能需要备考建议,比如如何,推荐哪些平台,如何提高解速度等。这部分需要结构清晰,分点说明。 需要确保回答真实可靠,所以提到的目类型和备考方法要有根据,避免错误信息。同时结构要清晰,分步骤介绍,比如先解释OD机试是什么,再讲型,然后例题,最后备考建议。 最后检查是否符合系统级指令,数学表达式是否正确使用,段落是否分明,用中文回答,没有Markdown格式。确保LaTeX语法正确,比如变量、公式的写法,避免使用不正确符号。</think>OD机试目通常指企业招聘中使用的在线编程测评(Online Assessment),主要考察算法能力、编码实现和逻辑思维。以下是逐步解析: --- ### 一、OD机试的常见型 1. **数据结构类** - 数组/字符串操作:如查找最长回文子串、两数之和 - 链表:反转链表、检测环形链表 - 树:二叉树遍历、最近公共祖先 - 示例题目: > 给定数组 $nums$ 和目标值 $target$,找出和为 $target$ 的两个数的索引 > **输入**:$nums = [2,7,11,15], target = 9$ > **输出**:$[0,1]$ 2. **动态规划** - 背包问、最长递增子序列 - 示例题目: > 求数组中最长递增子序列的长度 > **输入**:$nums = [10,9,2,5,3,7,101,18]$ > **输出**:4(子序列 [2,5,7,101]) 3. **图算法** - 最短路径(Dijkstra算法)、拓扑排序 - 示例题目: > 判断有向图是否存在环 > **输入**:节点数 $n=3$,边集合 $edges = [[0,1],[1,2],[2,0]]$ > **输出**:True 4. **数学与逻辑** - 质数判定、排列组合计算 - 示例题目: > 计算 $n$ 个骰子掷出总和为 $x$ 的概率 > **公式**: > $$ P(x) = \frac{\text{满足条件的组合数}}{6^n} $$ --- ### 二、目难度分布 - **初级**:考察基础语法和数据结构(如数组遍历) - **中级**:要求优化时间复杂度,常用贪心或动态规划(如 $O(n)$ 解决子序列问) - **高级**:涉及复杂模型(如多线程协同、图论算法) --- ### 三、备考建议 1. **平台** - LeetCode(按企业分类) - 牛客网(国内企业真) - Codeforces(提升思维敏捷度) 2. **重点掌握** - 熟悉常见算法模板(如二分查找、DFS/BFS) - 理解时间复杂度的计算与优化,例如: - 暴力解法:$O(n^2)$ - 优化后:$O(n \log n)$ 3. **模拟实战** - 限时练习(如2小时内完成3道) - 注意输入输出格式(尤其是大数处理) --- ### 四、典型例题解析(动态规划) **目**:爬楼梯问 - **描述**:每次爬1或2阶台阶,求到达第 $n$ 阶的方案数 - **递推公式**: $$ dp[i] = dp[i-1] + dp[i-2] $$ - **边界条件**: $$ dp[0] = 1, \quad dp[1] = 1 $$ --- 通过系统训练和针对性,可有效提升OD机试通过率。建议从简单入手,逐步掌握算法思想,再挑战高难度目。
评论 47
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

温文艾尔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值