LeetCode 分类刷题:404. 左叶子之和

题目

给定二叉树的根节点 root ,返回所有左叶子之和。

解析

我的思路

递:

从根节点开始,所有左叶子之和=左子树的左叶子之和+右子树的左叶子之和

归:

  1. 当节点为空时,返回0;

  2. 当节点不为空时:

已知当左右子树为空时,该节点是叶子节点。

问题在于如何判断是否为左叶子呢?

在遍历某个节点的子树时,需要添加一个标识:

  • 如果是左子树,标识为true;
  • 如果是右子树,标识为false。

当标识为true且左右子树为空时,返回该节点的值。

灵神思路

题意:如果一个节点的左儿子是叶子,那么把左儿子的节点值加到答案中。

在遍历二叉树的过程中,如果当前节点的左儿子是叶子,即左儿子的左右儿子均为空,那么把左儿子的节点值加到答案中。

代码直接把 sumOfLeftLeaves 当作递归函数使用。

作者:灵茶山艾府

链接:https://leetcode.cn/problems/sum-of-left-leaves/solutions/3751068/jian-dan-ti-jian-dan-zuo-pythonjavaccgoj-w8st/

来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

答案

我的答案:

javascript 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
function dfs(root, isleft) {
    if(root === null) return 0;
    if(isleft && root.left === null && root.right === null) return root.val;
    return dfs(root.left, true) + dfs(root.right, false);
}
var sumOfLeftLeaves = function(root) {
    // 递:每棵树的左叶子之和等于它的左右子树中的左叶子之和
    // 归:节点为空时返回0,位于左子树且该节点的左、右子树均为空时(为叶子节点)返回该节点的值
    return dfs(root.left, true) + dfs(root.right, false);
};

灵神答案:

javascript 复制代码
var sumOfLeftLeaves = function(root) {
    if (root === null) { // 空节点
        return 0;
    }

    // 递归左右子树,收集左右子树中的「节点的左儿子是叶子」的叶子节点值之和
    let sum = sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);

    const left = root.left; // 当前节点的左儿子
    if (left && left.left === null && left.right === null) { // 当前节点的左儿子是叶子
        sum += left.val; // 累加节点值
    }

    return sum;
};

// 作者:灵茶山艾府
// 链接:https://leetcode.cn/problems/sum-of-left-leaves/solutions/3751068/jian-dan-ti-jian-dan-zuo-pythonjavaccgoj-w8st/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

**我的疑惑:**为什么在递归调用之后还要加一个判断来累加 root.left.val?

因为 在递归中,当前节点并不知道自己的左子节点是不是左叶子,除非站在父节点的视角判断。

递归逻辑分解

  1. sumOfLeftLeaves(root.left)

    进入左子树,继续寻找左叶子(在左子树的视角里,它也会判断自己的左子节点是不是左叶子)。

  2. sumOfLeftLeaves(root.right)

    进入右子树,继续寻找左叶子。

  3. 但是,root 的左子节点 root.left 如果是叶子,它在它自己的递归调用中不会被计入,因为:

    • 当递归进入 root.left 时,它看到自己左右都为 null,但它不知道自己是父亲的左孩子还是右孩子。

    • 在 root.left 的递归中,root.left.left 和 root.left.right 都是 null,所以不会进入 if 条件,只会返回 0。

    • 所以 root.left 如果是左叶子,必须由它的父节点 (root) 来识别并累加。

关键理解

  • 递归调用 sumOfLeftLeaves(root.left) 返回的是以 root.left 为根的树中所有的左叶子之和,并不包括 root.left 本身(如果它是叶子)。

  • 判断一个节点是不是左叶子,必须由它的父节点来完成,因为需要知道它是左孩子还是右孩子,以及它是不是叶子。

  • 所以代码结构是:

    1. 递归左右子树,收集它们之下的左叶子之和。

    2. 检查当前节点的直接左孩子是不是左叶子,如果是,加上它的值。

复杂度分析

时间复杂度:O(n),其中 n 是二叉树的节点个数。

空间复杂度:O(n)。最坏情况下,二叉树退化成一条链,递归需要 O(n) 的栈空间。

相关推荐
子兮曰20 分钟前
把 649 种文件格式塞进一个网页里:一个「文件不出浏览器」的预览器
前端·javascript·后端
启观川3 小时前
数据结构与算法 -第 3 章 常用算法-动态规划
数据结构·笔记·python·算法
码爸3 小时前
排序算法介绍
python·算法·排序算法
paopaokaka_luck3 小时前
基于springboot+vue的智慧家政管理系统(AI问答、 WebSocket实时沟通、ECharts运营分析、订单规则与服务追踪)
java·javascript·spring boot·数据分析·echarts·mybatis
溪语流沙4 小时前
【每天一个CSS | Day19】流光文字与一笔写成的手写签名
前端·javascript·css
动词ing5 小时前
【题目练习】算法 组合总和||
算法
longlongzihan5 小时前
链表找环:Floyd 判圈算法(LeetCode 141 & 142 )
c++·算法·leetcode·链表
CDwenhuohuo5 小时前
electron pc项目打包成桌面端
前端·javascript·electron
专业程序开发源5 小时前
flask家电故障预测系统92491-计算机课程设计/毕业设计
vscode·python·sql·算法·flask·课程设计
Logic1015 小时前
C语言/数据结构欧几里得算法题解:长方形切割最大正方形——贪心划分计数
c语言·数据结构·算法·贪心算法·时间复杂度·空间复杂度·欧几里得算法