LeetCode 543 二叉树的直径:为什么求深度时顺手就能求出直径?

LeetCode 543 二叉树的直径:为什么求深度时顺手就能求出直径?

在刷二叉树题目的时候,经常会遇到一个现象:

求最大深度会写,求二叉树直径却总感觉绕。

其实这道题本质上还是一道「求深度」题,只不过在求深度的过程中,多维护了一个全局最大值。

今天就彻底搞懂这道经典面试题。


一、题目描述

给定一棵二叉树,返回它的直径长度。

这里的直径定义为:

任意两个节点之间最长路径的边数。

注意:

  • 计算的是边数,不是节点数;
  • 最长路径不一定经过根节点。

例如:

    1
   / \
  2   3
 / \
4   5

最长路径:

4 → 2 → 1 → 3

共有 3 条边,因此答案为:

3

二、先理解什么是深度

对于任意节点:

depth(node)

表示:

以 node 为根的子树最大深度。

例如:

    2
   / \
  4   5

节点 2 的深度:

max(左子树深度, 右子树深度) + 1

即:

max(depth(node.left), depth(node.right)) + 1

这正是 LeetCode 104 最大深度那道题的核心公式。


三、为什么直径可以用深度求出来?

关键观察:

对于任意一个节点来说:

左子树最深节点
      ↓
      node
      ↑
右子树最深节点

经过当前节点的最长路径一定是:

左侧最深叶子
    ↓
 当前节点
    ↑
右侧最深叶子

那么这条路径长度是多少?

答案:

左子树深度 + 右子树深度

例如:

    1
   / \
  2   3

节点 1:

左深度 = 1
右深度 = 1

经过节点 1 的路径长度:

1 + 1 = 2

刚好对应:

2 → 1 → 3

四、直径真正的本质

经过某个节点的最长路径:

left_depth + right_depth

那么整棵树的直径是什么?

答案就是:

所有节点中
(left_depth + right_depth)
的最大值

换句话说:

diameter = max(
    每个节点的左深度 + 右深度
)

因此:

我们只需要遍历整棵树,在计算深度的同时更新最大值即可。


五、递归实现

完整代码:

from typing import Optional

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:

        max_diameter = 0

        def depth(node):
            nonlocal max_diameter

            if not node:
                return 0

            left_depth = depth(node.left)
            right_depth = depth(node.right)

            # 更新当前最大直径
            max_diameter = max(
                max_diameter,
                left_depth + right_depth
            )

            # 返回当前子树深度
            return max(left_depth, right_depth) + 1

        depth(root)

        return max_diameter

六、代码执行过程

以这棵树为例:

    1
   / \
  2   3
 / \
4   5

递归过程:

节点4

left = 0
right = 0

更新直径:

0 + 0 = 0

返回深度:

1

节点5

同理:

返回深度 = 1

节点2

此时:

left_depth = 1
right_depth = 1

更新直径:

1 + 1 = 2

返回深度:

max(1,1)+1 = 2

节点3

返回:

1

节点1

此时:

left_depth = 2
right_depth = 1

更新直径:

2 + 1 = 3

最终答案:

3

七、为什么递归返回的是深度而不是直径?

很多同学第一次写这题都会犯这个错误。

递归函数的职责是:

返回当前子树深度

因为父节点需要利用左右子树深度:

left_depth
right_depth

来计算自己的深度。

如果返回直径:

return max_diameter

父节点根本无法计算自己的深度。

所以:

深度负责向上返回
直径负责全局记录

两者职责完全不同。


八、易错点总结

1. 把边数当成节点数

正确:

left_depth + right_depth

错误:

left_depth + right_depth + 1

加 1 后得到的是节点数,不是边数。


2. 只计算根节点

很多人会写:

depth(root.left) + depth(root.right)

直接返回。

这是错误的。

最长路径可能完全位于某棵子树内部。

必须遍历所有节点。


3. 返回值写错

递归函数必须返回:

max(left_depth, right_depth) + 1

而不是返回直径。


4. 忘记 nonlocal

内部函数修改外层变量:

max_diameter

必须声明:

nonlocal max_diameter

否则会出现作用域错误。


九、这道题和最大深度题的关系

很多人刷完这题都会有一个感悟:

二叉树的直径,本质上是「最大深度」题目的升级版。

LeetCode 104:

返回深度

LeetCode 543:

返回深度
+
顺手维护最大直径

所以掌握了最大深度以后,再来看直径问题就会简单很多。

很多二叉树题其实都是这种模式:

递归返回一个值
同时更新一个全局答案

比如:

  • 二叉树直径
  • 路径和
  • 平衡二叉树
  • 最大路径和

本质套路完全一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值