字节面试题——404. 左叶子之和

121 阅读3分钟

404. 左叶子之和

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

示例 1:

leftsum-tree.jpg

输入: root = [3,9,20,null,null,15,7] 
输出: 24 
解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

示例 2:

输入: root = [1]
输出: 0

提示:

  • 节点数在 [1, 1000] 范围内
  • -1000 <= Node.val <= 1000

无返回值的DFS,前序

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        dfs(root);
        return sum;
    }

    // 记录左叶子之和
    int sum = 0;
    // 二叉树遍历函数
    void dfs(TreeNode root) {
        if (root == null) {
            return;
        }

        // 前序位置,不能无条件累加当前节点的值,我们要算的是左叶子节点的和。注意这里操作的是当前节点和左子节点,不是只有当前节点。为什么要这样?因为左叶子节点=左+叶子,这一定需要两个节点才能判断。为什么不能通过当前节点和父节点判断?因为这样比较复杂。
        if (root.left != null &&
            root.left.left == null && root.left.right == null) {
            // 找到左侧的叶子节点,记录累加值
            sum += root.left.val;
        }

        // 递归框架
        dfs(root.left);
        dfs(root.right);
    }
}
  • 时间复杂度O(n),n是二叉树的节点总数
  • 空间复杂度O(h),h是二叉树的高度

有返回值的DFS

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        // 调用递归函数,并从根节点开始。注意根节点不是左节点。
        return dfs(root, false);
    }

    /**
     * 递归函数,计算左叶子节点之和
     * 
     * @param node 当前节点
     * @param isLeft 标记当前节点是否是左节点
     * @return 返回左叶子节点的和
     */
    private int dfs(TreeNode node, boolean isLeft) {
        if (node == null) {
            return 0; // 如果节点为空,返回 0
        }
        // 判断是否为左叶子节点
        if (node.left == null && node.right == null && isLeft) {
            return node.val; // 如果是左叶子节点,返回该节点的值
        }
        
        // 递归求取左子树和右子树的左叶子之和
        int leftSum = dfs(node.left, true);  // 左子树,标记为左节点
        int rightSum = dfs(node.right, false); // 右子树,标记为右节点
        
        // 返回左子树和右子树中所有左叶子节点的和
        return leftSum + rightSum;
    }
}
  • 时间复杂度O(n),n是二叉树的节点总数
  • 空间复杂度O(h),h是二叉树的高度

BFS

class Solution {
    /**
     * 计算二叉树中所有左叶子节点的和
     *
     * @param root 二叉树的根节点
     * @return 所有左叶子节点的和
     */
    public int sumOfLeftLeaves(TreeNode root) {
        if (root == null) {
            return 0; // 如果根节点为空,则返回0
        }

        Queue<TreeNode> queue = new LinkedList<>(); // 创建一个队列用于层次遍历
        queue.offer(root); // 将根节点添加到队列中
        int sum = 0; // 初始化左叶子节点和为0

        while (!queue.isEmpty()) {
            TreeNode currentNode = queue.poll(); // 从队列中取出当前节点

            // 检查左右子节点是否存在,只入队非叶子节点,因为没必要入队叶子节点
            if (currentNode.left != null) {
                // 检查左子节点是否为叶子节点
                if (isLeafNode(currentNode.left)) {
                    sum += currentNode.left.val; // 如果是叶子节点,则将其值累加到sum中。如果是叶子节点,则没必要加入队列了,只要把它的和算进去就好了,他没有子节点了。
                } else {
                    queue.offer(currentNode.left); // 否则将左子节点添加到队列中继续遍历
                }
            }
            // 检查右子节点是否存在且不是叶子节点,只入队非叶子节点的右子节点
            if (currentNode.right != null && !isLeafNode(currentNode.right)) {
                queue.offer(currentNode.right); // 将右子节点添加到队列中继续遍历
            }
        }

        return sum; // 返回左叶子节点的和
    }

    /**
     * 判断节点是否为叶子节点
     *
     * @param node 要判断的节点
     * @return 如果节点是叶子节点,则返回true,否则返回false
     */
    private boolean isLeafNode(TreeNode node) {
        return node.left == null && node.right == null; // 如果节点的左子节点和右子节点都为空,则为叶子节点
    }
}
  • 时间复杂度:O(n),其中 n 是树中的节点个数。
  • 空间复杂度:O(n)。空间复杂度与广度优先搜索使用的队列需要的容量相关,为 O(n)。