二叉树题目:好叶子结点对的数量

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:好叶子结点对的数量

出处:1530. 好叶子结点对的数量

难度

6 级

题目描述

要求

给定二叉树的根结点 root \texttt{root} root 和整数 distance \texttt{distance} distance。如果二叉树中两个不同的结点之间的最短路径长度小于或者等于 distance \texttt{distance} distance,那它们构成一组好叶子结点对。

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

示例

示例 1:

二叉树题目:好叶子结点对的数量_第1张图片

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

示例 2:

二叉树题目:好叶子结点对的数量_第2张图片

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

示例 3:

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

数据范围

  • 树中结点数目在范围 [1,   2 10 ] \texttt{[1, 2}^\texttt{10}\texttt{]} [1, 210]
  • 1 ≤ Node.val ≤ 100 \texttt{1} \le \texttt{Node.val} \le \texttt{100} 1Node.val100
  • 1 ≤ distance ≤ 10 \texttt{1} \le \texttt{distance} \le \texttt{10} 1distance10

解法

思路和算法

对于二叉树中的任意两个不同的叶结点,这两个叶结点之间的最短路径是唯一的,且一定经过最近公共祖先。对于每个结点,如果其左子树和右子树都非空,则在其左子树和右子树中各选一个叶结点,这两个叶结点的最近公共祖先即为当前结点,当前结点和两个叶结点的距离之和即为两个叶结点之间的最短路径长度。

为了计算好叶结点对的数量,需要对每个结点计算以当前结点为根结点的子树中的每个叶结点和当前结点的距离,并使用列表存储这些距离。由于好叶结点对满足最短路径长度不超过 distance \textit{distance} distance,因此列表中只需要存储不超过 distance \textit{distance} distance 的距离。可以使用深度优先搜索计算子树中每个叶结点和子树根结点的距离,具体做法如下。

  • 如果当前结点为空,则不存在叶结点,返回空列表。

  • 如果当前结点的左子结点和右子结点都为空,则当前结点为叶结点,叶结点和当前结点的距离为 0 0 0,在列表中添加元素 0 0 0 之后返回列表。

  • 对于其余情况,首先对左子树和右子树分别得到距离列表,对于每个非空子树的距离列表,其中的每个距离加 1 1 1 之后的结果即为叶结点到当前结点的距离,因此将每个非空子树的距离列表中的距离加 1 1 1 之后的值添加到当前结点的距离列表,然后返回列表。

在对每个结点计算子树中每个叶结点和子树根结点的距离的同时,即可计算好叶结点对的数量。如果一个结点的左子树和右子树都不为空,则遍历左子树和右子树的距离列表,并分别计算左子树和右子树中的每对叶结点之间的最短路径长度(同一对叶结点必须分别属于左子树和右子树)。如果一对叶结点之间的最短路径长度不超过 distance \textit{distance} distance,则将好叶结点对的数量加 1 1 1

遍历结束之后,即可得到二叉树中的好叶结点对的数量。

代码

class Solution {
    int pairs = 0;

    public int countPairs(TreeNode root, int distance) {
        getDistances(root, distance);
        return pairs;
    }

    public List<Integer> getDistances(TreeNode node, int distance) {
        List<Integer> distances = new ArrayList<Integer>();
        if (node == null) {
            return distances;
        }
        if (node.left == null && node.right == null) {
            distances.add(0);
            return distances;
        }
        List<Integer> leftDistances = getDistances(node.left, distance);
        List<Integer> rightDistances = getDistances(node.right, distance);
        int leftSize = leftDistances.size();
        int rightSize = rightDistances.size();
        for (int i = 0; i < leftSize; i++) {
            int leftDistance = leftDistances.get(i) + 1;
            leftDistances.set(i, leftDistance);
            if (leftDistance <= distance) {
                distances.add(leftDistance);
            }
        }
        for (int i = 0; i < rightSize; i++) {
            int rightDistance = rightDistances.get(i) + 1;
            rightDistances.set(i, rightDistance);
            if (rightDistance <= distance) {
                distances.add(rightDistance);
            }
        }
        for (int leftDistance : leftDistances) {
            for (int rightDistance : rightDistances) {
                if (leftDistance + rightDistance <= distance) {
                    pairs++;
                }
            }
        }
        return distances;
    }
}

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是二叉树的结点数。每个结点都被访问一次,对于每一对叶结点都需要计算最短路径长度。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是存储距离的列表和递归调用的栈空间。

你可能感兴趣的:(数据结构和算法,#,树,树,二叉树)