(LeetCodeHot100)543. 二叉树的直径——diameter-of-binary-tree

543. 二叉树的直径——diameter-of-binary-tree

给你一棵二叉树的根节点,返回该树的 直径

二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root

两节点之间路径的 长度 由它们之间边数表示。

示例 1:

img

1
2
3
输入:root = [1,2,3,4,5]
输出:3
解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。

示例 2:

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

提示:

  • 树中节点数目在范围 [1, 104]
  • -100 <= Node.val <= 100

  • 一看到这个题目一头雾水,之前只会求最大深度
  • 这题确实会用到最大深度:
  • 两个叶子结点之间路径=根节点左右儿子的深度之和
  • 但是会出现这个问题:

image-20251113201629407

  • 所以跟之前的求最大深度不一样:要加一个ans,每次过一个结点就求最大路径(深度之和),每次就比较一次,把大的放入ans

方法:深度优先搜索

首先我们知道一条路径的长度为该路径经过的节点数减一,所以求直径(即求路径长度的最大值)等效于求路径经过节点数的最大值减一。

而任意一条路径均可以被看作由某个节点为起点,从其左儿子和右儿子向下遍历的路径拼接得到。

543.jpg

核心逻辑

二叉树直径 = 树中任意两节点最长路径的边数 → 等效于「最长路径的节点数 - 1」。

关键观察

任意一条最长路径,必然以某个节点为「中心」,由该节点的「左子树最深路径」和「右子树最深路径」拼接而成(即:左路径 + 中心节点 + 右路径)。

算法步骤(递归)

  1. 定义递归函数 depth(node)
    • 输入:当前节点 node
    • 输出:以 node 为根的子树的「深度」(即从 node 到最远叶子的节点数)
    • 隐藏作用:计算以 node 为中心的最长路径节点数,更新全局最大值。
  2. 递归计算
    • nodenull,返回深度 0(空树无节点)。
    • 递归左子树,得到左子树深度 L
    • 递归右子树,得到右子树深度 R
  3. 更新最长路径
    • 当前节点为中心的路径节点数 = L + R + 1(左路径节点数 + 右路径节点数 + 中心节点)。
    • 用这个值更新全局变量 ans(记录所有节点中最大的路径节点数)。
  4. 返回子树深度
    • node 为根的子树深度 = max(L, R) + 1(取左右子树较深的一个,加当前节点)。
  5. 最终结果
    • 直径 = 最长路径节点数(ans) - 1(节点数转边数)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
int ans;
public int diameterOfBinaryTree(TreeNode root) {
ans = 1;
depth(root);
return ans - 1;
}
public int depth(TreeNode node) {
if (node == null) {
return 0; // 访问到空节点了,返回0
}
int L = depth(node.left); // 左儿子为根的子树的深度
int R = depth(node.right); // 右儿子为根的子树的深度
ans = Math.max(ans, L+R+1); // 计算d_node即L+R+1 并更新ans
return Math.max(L, R) + 1; // 返回该节点为根的子树的深度
}
}

复杂度分析

  • 时间复杂度:O(N),其中 N 为二叉树的节点数,即遍历一棵二叉树的时间复杂度,每个结点只被访问一次。
  • 空间复杂度:O(Height),其中Height 为二叉树的高度。由于递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,而递归的深度显然为二叉树的高度,并且每次递归调用的函数里又只用了常数个变量,所以所需空间复杂度为 O(Height) 。