leetcode
leetcode 1401 ~ 1450
好叶子节点对的数量

好叶子节点对的数量

难度:

标签:

题目描述

给你二叉树的根节点 root 和一个整数 distance

如果二叉树中两个 节点之间的 最短路径长度 小于或者等于 distance ,那它们就可以构成一组 好叶子节点对

返回树中 好叶子节点对的数量

 

示例 1:

 

输入:root = [1,2,3,null,4], distance = 3
输出:1
解释:树的叶节点是 3 和 4 ,它们之间的最短路径的长度是 3 。这是唯一的好叶子节点对。

示例 2:

输入:root = [1,2,3,4,5,6,7], distance = 3
输出:2
解释:好叶子节点对为 [4,5] 和 [6,7] ,最短路径长度都是 2 。但是叶子节点对 [4,6] 不满足要求,因为它们之间的最短路径长度为 4 。

示例 3:

输入:root = [7,1,4,6,null,5,3,null,null,null,null,null,2], distance = 3
输出:1
解释:唯一的好叶子节点对是 [2,5] 。

示例 4:

输入:root = [100], distance = 1
输出:0

示例 5:

输入:root = [1,1,1], distance = 2
输出:1

 

提示:

  • tree 的节点数在 [1, 2^10] 范围内。
  • 每个节点的值都在 [1, 100] 之间。
  • 1 <= distance <= 10

代码结果

运行时间: 63 ms, 内存: 16.4 MB


// 思路:
// 1. 使用递归的方式找到所有叶子节点,并计算它们到根节点的路径长度。
// 2. 将所有叶子节点的距离存储在一个列表中,使用流操作来过滤和计数满足条件的叶子节点对。

import java.util.*;
import java.util.stream.*;

class Solution {
    public int countPairs(TreeNode root, int distance) {
        List<Integer> leafDistances = new ArrayList<>();
        calculateLeafDistances(root, 0, leafDistances);
        return (int) IntStream.range(0, leafDistances.size()).boxed()
            .flatMap(i -> IntStream.range(i + 1, leafDistances.size())
            .mapToObj(j -> Math.abs(leafDistances.get(i) - leafDistances.get(j))))
            .filter(d -> d <= distance)
            .count();
    }

    private void calculateLeafDistances(TreeNode node, int currentDistance, List<Integer> leafDistances) {
        if (node == null) return;
        if (node.left == null && node.right == null) {
            leafDistances.add(currentDistance);
            return;
        }
        calculateLeafDistances(node.left, currentDistance + 1, leafDistances);
        calculateLeafDistances(node.right, currentDistance + 1, leafDistances);
    }
}

解释

方法:

该题解采用了深度优先搜索(DFS)来遍历二叉树。在每个节点,如果它是叶子节点,则返回包含其深度的列表。如果不是叶子节点,则递归地处理左右子节点。将返回的左右子节点的深度列表进行比较,对于左子树的每个深度,检查右子树中与之可以配对形成好叶子节点对的深度(即两者深度之和不超过distance)。在遍历过程中统计这些好叶子节点对的数量。最终,返回总的好叶子节点对数。

时间复杂度:

O(n^2 log n)

空间复杂度:

O(n)

代码细节讲解

🦆
在DFS函数中,为什么选择返回叶子节点的深度列表,而不是直接计算在该节点以下的好叶子节点对数?
在DFS函数中返回叶子节点的深度列表而不是直接计算好叶子节点对数的主要原因是为了将问题分解成可管理的部分,从而简化问题的复杂度。这种方法允许在每个节点处仅考虑其子树的叶子节点深度信息,而不是整个树的结构。这样,每个节点作为中介可以独立地评估与其左右子节点相关的所有可能的好叶子节点对。如果直接在每个节点下计算好叶子节点对数,将需要更复杂的状态管理和可能导致重复计数问题,因为相同的叶子对可能在树的多个级别被计算多次。通过返回深度列表,可以有效地在每个节点的上下文中只计算一次,且确保每对叶子只被统计一次。
🦆
题解中的break语句在内外循环中起到了什么作用?是否存在某些情况下,这些break语句没有正确地跳出循环?
题解中的break语句用于优化算法性能,避免不必要的比较。在内层循环中,当发现叶子节点对的总深度超过允许的最大distance时,break语句会终止当前的比较,因为右子树的深度列表是排序的,一旦当前组合深度超过distance,后续的组合也一定超过,因此没有继续检查的必要。在外层循环中,break语句的逻辑应是在深度已经达到或超过distance时停止循环,但从代码实现来看,外层循环的break语句实际上并未起到应有的效果,因为它是基于从父节点的深度计算,而不是从根节点。实际上,这个break可能不会在所有情况下正确地终止循环,特别是当左子树的叶子深度较小,但右子树深度较大且未排序的情况下。

相关问题