|
5 | 5 |
|
6 | 6 | ## 题目大意
|
7 | 7 |
|
8 | | -**描述**:给一个二叉树的根节点 `root`。 |
| 8 | +**描述**:给一个二叉树的根节点 $root$。 |
9 | 9 |
|
10 | 10 | **要求**:计算该二叉树的直径长度。
|
11 | 11 |
|
12 | 12 | **说明**:
|
13 | 13 |
|
14 | 14 | - **二叉树的直径长度**:二叉树中任意两个节点路径长度中的最大值。
|
15 | 15 | - 两节点之间的路径长度是以它们之间边的数目表示。
|
| 16 | +- 这条路径可能穿过也可能不穿过根节点。 |
16 | 17 |
|
17 | 18 | **示例**:
|
18 | 19 |
|
|
31 | 32 |
|
32 | 33 | ## 解题思路
|
33 | 34 |
|
34 | | -### 思路 1:深度优先搜索 |
| 35 | +### 思路 1:树形 DP + 深度优先搜索 |
35 | 36 |
|
36 | | -这道题的重点是理解直径长度的定义。这里的直径并不是简单的「左子树高度」+「右子树高度」。 |
| 37 | +这道题重点是理解直径长度的定义。「二叉树的直径长度」的定义为:二叉树中任意两个节点路径长度中的最大值。并且这条路径可能穿过也可能不穿过根节点。 |
37 | 38 |
|
38 | | -而是 `当前节点的直径 = max{左子树高度+右子树高度,所有子树中最大直径}`。 |
| 39 | +对于根为 $root$ 的二叉树来说,其直径长度并不简单等于「左子树高度」加上「右子树高度」。 |
39 | 40 |
|
40 | | -也就是说当前节点的直径可能来自于 「左子树高度」+「右子树高度」,也可能来自于「子树中的最大直径」。 |
| 41 | +根据路径是否穿过根节点,我们可以将二叉树分为两种: |
41 | 42 |
|
42 | | -这就需要在递归求解子树高度的时候维护一个 `maxDiameter` 变量。每次递归都要去判断 当前「左子树高度」+「右子树的高度」是否大于 `self.maxDiameter`,如果大于,则更新最大值。 |
| 43 | +1. 直径长度所对应的路径穿过根节点。 |
| 44 | +2. 直径长度所对应的路径不穿过根节点。 |
| 45 | + |
| 46 | +我们来看下图中的两个例子。 |
| 47 | + |
| 48 | + |
| 49 | + |
| 50 | +如图所示,左侧这棵二叉树就是一棵常见的平衡二叉树,其直径长度所对应的路径是穿过根节点的($D\rightarrow B \rightarrow A \rightarrow C$)。这种情况下:$二叉树的直径 = 左子树高度 + 右子树高度$。 |
| 51 | + |
| 52 | +而右侧这棵特殊的二叉树,其直径长度所对应的路径是没有穿过根节点的($F \rightarrow D \rightarrow B \rightarrow E \rightarrow G$)。这种情况下:$二叉树的直径 = 所有子树中最大直径长度$。 |
| 53 | + |
| 54 | +也就是说根为 $root$ 的二叉树的直径长度可能来自于 $左子树高度 + 右子树高度,ドル也可能来自于 $子树中的最大直径,ドル即 $二叉树的直径 = max(左子树高度 + 右子树高度, \quad 所有子树中最大直径长度)$。 |
| 55 | + |
| 56 | +那么现在问题就变成为如何求「子树的高度」和「子树中的最大直径」。 |
| 57 | + |
| 58 | +1. 子树的高度:我们可以利用深度优先搜索方法,递归遍历左右子树,并分别返回左右子树的高度。 |
| 59 | +2. 子树中的最大直径:我们可以在递归求解子树高度的时候维护一个 $ans$ 变量,用于记录所有 $左子树高度 + 右子树高度$ 中的最大值。 |
| 60 | + |
| 61 | +最终 $ans$ 就是我们所求的该二叉树的最大直径,将其返回即可。 |
43 | 62 |
|
44 | 63 | ### 思路 1:代码
|
45 | 64 |
|
46 | 65 | ```Python
|
| 66 | +# Definition for a binary tree node. |
| 67 | +# class TreeNode: |
| 68 | +# def __init__(self, val=0, left=None, right=None): |
| 69 | +# self.val = val |
| 70 | +# self.left = left |
| 71 | +# self.right = right |
47 | 72 | class Solution:
|
48 | 73 | def __init__(self):
|
49 | | - # 保存当前最大直径 |
50 | | - self.maxDiameter = 0 |
| 74 | + self.ans = 0 |
51 | 75 |
|
52 | | - def diameterOfBinaryTree(self, root: TreeNode) -> int: |
53 | | - self.height(root) |
54 | | - return self.maxDiameter |
55 | | - |
56 | | - def height(self, root): |
57 | | - if root == None: |
| 76 | + def dfs(self, node): |
| 77 | + if not node: |
58 | 78 | return 0
|
59 | | - leftHeight = self.height(root.left) |
60 | | - rightHeight = self.height(root.right) |
61 | | - self.maxDiameter = max(self.maxDiameter, leftHeight + rightHeight) |
62 | | - |
63 | | - return max(leftHeight, rightHeight) + 1 |
| 79 | + left_height = self.dfs(node.left) # 左子树高度 |
| 80 | + right_height = self.dfs(node.right) # 右子树高度 |
| 81 | + self.ans = max(self.ans, left_height + right_height) # 维护所有路径中的最大直径 |
| 82 | + return max(left_height, right_height) + 1 # 返回该节点的高度 = 左右子树最大高度 + 1 |
| 83 | + |
| 84 | + def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int: |
| 85 | + self.dfs(root) |
| 86 | + return self.ans |
64 | 87 | ```
|
65 | 88 |
|
66 | 89 | ### 思路 1:复杂度分析
|
|
0 commit comments